Skip to content

Commit 6770af1

Browse files
committed
Merge pull request square#292 from aravance/master
Enable resource loading from android.resource URI.
2 parents 6914a8e + 58adc93 commit 6770af1

File tree

5 files changed

+129
-6
lines changed

5 files changed

+129
-6
lines changed

picasso/src/main/java/com/squareup/picasso/ResourceBitmapHunter.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,23 @@ class ResourceBitmapHunter extends BitmapHunter {
3333
}
3434

3535
@Override Bitmap decode(Request data) throws IOException {
36-
return decodeResource(context.getResources(), data);
36+
Resources res = Utils.getResources(context, data);
37+
int id = Utils.getResourceId(res, data);
38+
return decodeResource(res, id, data);
3739
}
3840

3941
@Override Picasso.LoadedFrom getLoadedFrom() {
4042
return DISK;
4143
}
4244

43-
private Bitmap decodeResource(Resources resources, Request data) {
44-
int resourceId = data.resourceId;
45+
private Bitmap decodeResource(Resources resources, int id, Request data) {
4546
BitmapFactory.Options bitmapOptions = null;
4647
if (data.hasSize()) {
4748
bitmapOptions = new BitmapFactory.Options();
4849
bitmapOptions.inJustDecodeBounds = true;
49-
BitmapFactory.decodeResource(resources, resourceId, bitmapOptions);
50+
BitmapFactory.decodeResource(resources, id, bitmapOptions);
5051
calculateInSampleSize(data.targetWidth, data.targetHeight, bitmapOptions);
5152
}
52-
return BitmapFactory.decodeResource(resources, resourceId, bitmapOptions);
53+
return BitmapFactory.decodeResource(resources, id, bitmapOptions);
5354
}
5455
}

picasso/src/main/java/com/squareup/picasso/Utils.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,18 @@
2020
import android.content.ContentResolver;
2121
import android.content.Context;
2222
import android.content.pm.PackageManager;
23+
import android.content.res.Resources;
2324
import android.graphics.Bitmap;
2425
import android.os.Looper;
2526
import android.os.Process;
2627
import android.os.StatFs;
2728
import android.provider.Settings;
2829
import java.io.ByteArrayOutputStream;
2930
import java.io.File;
31+
import java.io.FileNotFoundException;
3032
import java.io.IOException;
3133
import java.io.InputStream;
34+
import java.util.List;
3235
import java.util.concurrent.ThreadFactory;
3336

3437
import static android.content.Context.ACTIVITY_SERVICE;
@@ -228,6 +231,50 @@ static boolean isWebPFile(InputStream stream) throws IOException {
228231
return isWebPFile;
229232
}
230233

234+
static int getResourceId(Resources resources, Request data) throws FileNotFoundException {
235+
if (data.resourceId != 0 || data.uri == null) {
236+
return data.resourceId;
237+
}
238+
239+
String pkg = data.uri.getAuthority();
240+
if (pkg == null) throw new FileNotFoundException("No package provided: " + data.uri);
241+
242+
int id;
243+
List<String> segments = data.uri.getPathSegments();
244+
if (segments == null || segments.isEmpty()) {
245+
throw new FileNotFoundException("No path segments: " + data.uri);
246+
} else if (segments.size() == 1) {
247+
try {
248+
id = Integer.parseInt(segments.get(0));
249+
} catch (NumberFormatException e) {
250+
throw new FileNotFoundException("Last path segment is not a resource ID: " + data.uri);
251+
}
252+
} else if (segments.size() == 2) {
253+
String type = segments.get(0);
254+
String name = segments.get(1);
255+
256+
id = resources.getIdentifier(name, type, pkg);
257+
} else {
258+
throw new FileNotFoundException("More than two path segments: " + data.uri);
259+
}
260+
return id;
261+
}
262+
263+
static Resources getResources(Context context, Request data) throws FileNotFoundException {
264+
if (data.resourceId != 0 || data.uri == null) {
265+
return context.getResources();
266+
}
267+
268+
String pkg = data.uri.getAuthority();
269+
if (pkg == null) throw new FileNotFoundException("No package provided: " + data.uri);
270+
try {
271+
PackageManager pm = context.getPackageManager();
272+
return pm.getResourcesForApplication(pkg);
273+
} catch (PackageManager.NameNotFoundException e) {
274+
throw new FileNotFoundException("Unable to obtain resources for package: " + data.uri);
275+
}
276+
}
277+
231278
@TargetApi(HONEYCOMB)
232279
private static class ActivityManagerHoneycomb {
233280
static int getLargeMemoryClass(ActivityManager activityManager) {

picasso/src/test/java/com/squareup/picasso/BitmapHunterTest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@
4848
import static com.squareup.picasso.TestUtils.MEDIA_STORE_CONTENT_KEY_1;
4949
import static com.squareup.picasso.TestUtils.RESOURCE_ID_1;
5050
import static com.squareup.picasso.TestUtils.RESOURCE_ID_KEY_1;
51+
import static com.squareup.picasso.TestUtils.RESOURCE_ID_URI;
52+
import static com.squareup.picasso.TestUtils.RESOURCE_TYPE_URI;
53+
import static com.squareup.picasso.TestUtils.RESOURCE_ID_URI_KEY;
54+
import static com.squareup.picasso.TestUtils.RESOURCE_TYPE_URI_KEY;
5155
import static com.squareup.picasso.TestUtils.URI_1;
5256
import static com.squareup.picasso.TestUtils.URI_KEY_1;
5357
import static com.squareup.picasso.TestUtils.mockAction;
@@ -227,6 +231,20 @@ public class BitmapHunterTest {
227231
assertThat(hunter).isInstanceOf(ResourceBitmapHunter.class);
228232
}
229233

234+
@Test public void forAndroidResourceUriWithId() throws Exception {
235+
Action action = mockAction(RESOURCE_ID_URI_KEY, RESOURCE_ID_URI);
236+
BitmapHunter hunter =
237+
forRequest(context, picasso, dispatcher, cache, stats, action, downloader);
238+
assertThat(hunter).isInstanceOf(ResourceBitmapHunter.class);
239+
}
240+
241+
@Test public void forAndroidResourceUriWithType() throws Exception {
242+
Action action = mockAction(RESOURCE_TYPE_URI_KEY, RESOURCE_TYPE_URI);
243+
BitmapHunter hunter =
244+
forRequest(context, picasso, dispatcher, cache, stats, action, downloader);
245+
assertThat(hunter).isInstanceOf(ResourceBitmapHunter.class);
246+
}
247+
230248
@Test public void forAssetRequest() {
231249
Action action = mockAction(ASSET_KEY_1, ASSET_URI_1);
232250
BitmapHunter hunter =

picasso/src/test/java/com/squareup/picasso/TestUtils.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
*/
1616
package com.squareup.picasso;
1717

18+
import android.content.Context;
19+
import android.content.pm.PackageManager;
20+
import android.content.res.Resources;
1821
import android.graphics.Bitmap;
1922
import android.net.NetworkInfo;
2023
import android.net.Uri;
@@ -24,9 +27,11 @@
2427
import org.mockito.invocation.InvocationOnMock;
2528
import org.mockito.stubbing.Answer;
2629

30+
import static android.content.ContentResolver.SCHEME_ANDROID_RESOURCE;
2731
import static android.provider.ContactsContract.Contacts.CONTENT_URI;
2832
import static android.provider.ContactsContract.Contacts.Photo.CONTENT_DIRECTORY;
2933
import static com.squareup.picasso.Utils.createKey;
34+
import static org.mockito.Mockito.doReturn;
3035
import static org.mockito.Mockito.mock;
3136
import static org.mockito.Mockito.when;
3237

@@ -55,11 +60,42 @@ class TestUtils {
5560
static final String CONTACT_KEY_1 = createKey(new Request.Builder(CONTACT_URI_1).build());
5661
static final Uri CONTACT_PHOTO_URI_1 =
5762
CONTENT_URI.buildUpon().path("1234").path(CONTENT_DIRECTORY).build();
58-
static final String CONTACT_PHOTO_KEY_1 = createKey(new Request.Builder(CONTACT_PHOTO_URI_1).build());
63+
static final String CONTACT_PHOTO_KEY_1 =
64+
createKey(new Request.Builder(CONTACT_PHOTO_URI_1).build());
5965
static final int RESOURCE_ID_1 = 1;
6066
static final String RESOURCE_ID_KEY_1 = createKey(new Request.Builder(RESOURCE_ID_1).build());
6167
static final Uri ASSET_URI_1 = Uri.parse("file:///android_asset/foo/bar.png");
6268
static final String ASSET_KEY_1 = createKey(new Request.Builder(ASSET_URI_1).build());
69+
static final String RESOURCE_PACKAGE = "com.squareup.picasso";
70+
static final String RESOURCE_TYPE = "drawable";
71+
static final String RESOURCE_NAME = "foo";
72+
static final Uri RESOURCE_ID_URI = new Uri.Builder().scheme(SCHEME_ANDROID_RESOURCE)
73+
.authority(RESOURCE_PACKAGE)
74+
.appendPath(Integer.toString(RESOURCE_ID_1))
75+
.build();
76+
static final String RESOURCE_ID_URI_KEY = createKey(new Request.Builder(RESOURCE_ID_URI).build());
77+
static final Uri RESOURCE_TYPE_URI = new Uri.Builder().scheme(SCHEME_ANDROID_RESOURCE)
78+
.authority(RESOURCE_PACKAGE)
79+
.appendPath(RESOURCE_TYPE)
80+
.appendPath(RESOURCE_NAME)
81+
.build();
82+
static final String RESOURCE_TYPE_URI_KEY =
83+
createKey(new Request.Builder(RESOURCE_TYPE_URI).build());
84+
85+
static Context mockPackageResourceContext() {
86+
Context context = mock(Context.class);
87+
PackageManager pm = mock(PackageManager.class);
88+
Resources res = mock(Resources.class);
89+
90+
doReturn(pm).when(context).getPackageManager();
91+
try {
92+
doReturn(res).when(pm).getResourcesForApplication(RESOURCE_PACKAGE);
93+
} catch (PackageManager.NameNotFoundException e) {
94+
throw new RuntimeException(e);
95+
}
96+
doReturn(RESOURCE_ID_1).when(res).getIdentifier(RESOURCE_NAME, RESOURCE_TYPE, RESOURCE_PACKAGE);
97+
return context;
98+
}
6399

64100
static Action mockAction(String key, Uri uri) {
65101
return mockAction(key, uri, null, 0);

picasso/src/test/java/com/squareup/picasso/UtilsTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,20 @@
1515
*/
1616
package com.squareup.picasso;
1717

18+
import java.io.IOException;
19+
20+
import android.content.res.Resources;
1821
import org.junit.Test;
1922
import org.junit.runner.RunWith;
2023
import org.robolectric.RobolectricTestRunner;
2124
import org.robolectric.annotation.Config;
2225
import java.io.ByteArrayInputStream;
2326

27+
import static com.squareup.picasso.TestUtils.RESOURCE_ID_1;
28+
import static com.squareup.picasso.TestUtils.RESOURCE_ID_URI;
29+
import static com.squareup.picasso.TestUtils.RESOURCE_TYPE_URI;
2430
import static com.squareup.picasso.TestUtils.URI_1;
31+
import static com.squareup.picasso.TestUtils.mockPackageResourceContext;
2532
import static com.squareup.picasso.Utils.createKey;
2633
import static com.squareup.picasso.Utils.isWebPFile;
2734
import static com.squareup.picasso.Utils.parseResponseSourceHeader;
@@ -87,4 +94,18 @@ public class UtilsTest {
8794
assertThat(isWebPFile(new ByteArrayInputStream("RIFFxxxxABCD".getBytes("US-ASCII")))).isFalse();
8895
assertThat(isWebPFile(new ByteArrayInputStream("RIFFxxWEBP".getBytes("US-ASCII")))).isFalse();
8996
}
97+
98+
@Test public void getResourceById() throws IOException {
99+
Request request = new Request.Builder(RESOURCE_ID_URI).build();
100+
Resources resources = Utils.getResources(mockPackageResourceContext(), request);
101+
int id = Utils.getResourceId(resources, request);
102+
assertThat(id).isEqualTo(RESOURCE_ID_1);
103+
}
104+
105+
@Test public void getResourceByTypeAndName() throws IOException {
106+
Request request = new Request.Builder(RESOURCE_TYPE_URI).build();
107+
Resources resources = Utils.getResources(mockPackageResourceContext(), request);
108+
int id = Utils.getResourceId(resources, request);
109+
assertThat(id).isEqualTo(RESOURCE_ID_1);
110+
}
90111
}

0 commit comments

Comments
 (0)