1 /*
2  * Copyright (C) 2014 Square, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.squareup.picasso;
17 
18 import android.content.ContentResolver;
19 import android.content.Context;
20 import android.database.Cursor;
21 import android.graphics.Bitmap;
22 import android.graphics.BitmapFactory;
23 import android.net.Uri;
24 import android.provider.MediaStore;
25 import java.io.IOException;
26 
27 import static android.content.ContentUris.parseId;
28 import static android.provider.MediaStore.Images.Thumbnails.FULL_SCREEN_KIND;
29 import static android.provider.MediaStore.Images.Thumbnails.MICRO_KIND;
30 import static android.provider.MediaStore.Images.Thumbnails.MINI_KIND;
31 import static android.provider.MediaStore.Images.Thumbnails.getThumbnail;
32 import static com.squareup.picasso.MediaStoreBitmapHunter.PicassoKind.FULL;
33 import static com.squareup.picasso.MediaStoreBitmapHunter.PicassoKind.MICRO;
34 import static com.squareup.picasso.MediaStoreBitmapHunter.PicassoKind.MINI;
35 
36 class MediaStoreBitmapHunter extends ContentStreamBitmapHunter {
37   private static final String[] CONTENT_ORIENTATION = new String[] {
38       MediaStore.Images.ImageColumns.ORIENTATION
39   };
40 
MediaStoreBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache, Stats stats, Action action)41   MediaStoreBitmapHunter(Context context, Picasso picasso, Dispatcher dispatcher, Cache cache,
42       Stats stats, Action action) {
43     super(context, picasso, dispatcher, cache, stats, action);
44   }
45 
decode(Request data)46   @Override Bitmap decode(Request data) throws IOException {
47     ContentResolver contentResolver = context.getContentResolver();
48     setExifRotation(getExitOrientation(contentResolver, data.uri));
49 
50     if (data.hasSize()) {
51       PicassoKind picassoKind = getPicassoKind(data.targetWidth, data.targetHeight);
52       if (picassoKind == FULL) {
53         return super.decode(data);
54       }
55 
56       long id = parseId(data.uri);
57 
58       BitmapFactory.Options options = new BitmapFactory.Options();
59       options.inJustDecodeBounds = true;
60 
61       calculateInSampleSize(data.targetWidth, data.targetHeight, picassoKind.width,
62           picassoKind.height, options);
63 
64       Bitmap result = getThumbnail(contentResolver, id, picassoKind.androidKind, options);
65 
66       if (result != null) {
67         return result;
68       }
69     }
70 
71     return super.decode(data);
72   }
73 
getPicassoKind(int targetWidth, int targetHeight)74   static PicassoKind getPicassoKind(int targetWidth, int targetHeight) {
75     if (targetWidth <= MICRO.width && targetHeight <= MICRO.height) {
76       return MICRO;
77     } else if (targetWidth <= MINI.width && targetHeight <= MINI.height) {
78       return MINI;
79     }
80     return FULL;
81   }
82 
getExitOrientation(ContentResolver contentResolver, Uri uri)83   static int getExitOrientation(ContentResolver contentResolver, Uri uri) {
84     Cursor cursor = null;
85     try {
86       cursor = contentResolver.query(uri, CONTENT_ORIENTATION, null, null, null);
87       if (cursor == null || !cursor.moveToFirst()) {
88         return 0;
89       }
90       return cursor.getInt(0);
91     } catch (RuntimeException ignored) {
92       // If the orientation column doesn't exist, assume no rotation.
93       return 0;
94     } finally {
95       if (cursor != null) {
96         cursor.close();
97       }
98     }
99   }
100 
101   enum PicassoKind {
102     MICRO(MICRO_KIND, 96, 96),
103     MINI(MINI_KIND, 512, 384),
104     FULL(FULL_SCREEN_KIND, -1, -1);
105 
106     final int androidKind;
107     final int width;
108     final int height;
109 
PicassoKind(int androidKind, int width, int height)110     PicassoKind(int androidKind, int width, int height) {
111       this.androidKind = androidKind;
112       this.width = width;
113       this.height = height;
114     }
115   }
116 }
117