Skip to content

Commit

Permalink
Fix handling of system and type/name resource Uris.
Browse files Browse the repository at this point in the history
-------------
Fix some broken javadoc.

-------------
Created by MOE: https://github.com/google/moe

MOE_MIGRATED_REVID=7b95ebeab7e0f80dda2ad89fb77c342f20cf3980

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=224180751
  • Loading branch information
sjudd committed Dec 19, 2018
1 parent be28a4d commit e2df4d0
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ public void load_withApplicationIconResourceIdUri_asBitmap_producesNonNullBitmap

@Test
public void load_withApplicationIconResourceIdUri_asBitmap_withTransformation_nonNullBitmap()
throws NameNotFoundException, ExecutionException, InterruptedException {
throws ExecutionException, InterruptedException {
for (String packageName : getInstalledPackages()) {
int iconResourceId = getResourceId(packageName);

Expand Down Expand Up @@ -426,9 +426,8 @@ public void load_withApplicationIconResourceNameUri_asDrawable_producesNonNullDr
Uri uri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(packageName)
.path(resources.getResourceTypeName(iconResourceId))
.path(resources.getResourceEntryName(iconResourceId))
.path(String.valueOf(iconResourceId))
.appendPath(resources.getResourceTypeName(iconResourceId))
.appendPath(resources.getResourceEntryName(iconResourceId))
.build();

Drawable drawable = Glide.with(context)
Expand All @@ -439,7 +438,6 @@ public void load_withApplicationIconResourceNameUri_asDrawable_producesNonNullDr
}
}


@Test
public void load_withApplicationIconResourceNameUri_asDrawable_withTransform_nonNullDrawable()
throws ExecutionException, InterruptedException, NameNotFoundException {
Expand All @@ -451,9 +449,8 @@ public void load_withApplicationIconResourceNameUri_asDrawable_withTransform_non
Uri uri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(packageName)
.path(resources.getResourceTypeName(iconResourceId))
.path(resources.getResourceEntryName(iconResourceId))
.path(String.valueOf(iconResourceId))
.appendPath(resources.getResourceTypeName(iconResourceId))
.appendPath(resources.getResourceEntryName(iconResourceId))
.build();

Drawable drawable = Glide.with(context)
Expand All @@ -476,9 +473,8 @@ public void load_withApplicationIconResourceNameUri_asBitmap_producesNonNullBitm
Uri uri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(packageName)
.path(resources.getResourceTypeName(iconResourceId))
.path(resources.getResourceEntryName(iconResourceId))
.path(String.valueOf(iconResourceId))
.appendPath(resources.getResourceTypeName(iconResourceId))
.appendPath(resources.getResourceEntryName(iconResourceId))
.build();

Bitmap bitmap = Glide.with(context)
Expand All @@ -501,9 +497,8 @@ public void load_withApplicationIconResourceNameUri_asBitmap_withTransform_nonNu
Uri uri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
.authority(packageName)
.path(resources.getResourceTypeName(iconResourceId))
.path(resources.getResourceEntryName(iconResourceId))
.path(String.valueOf(iconResourceId))
.appendPath(resources.getResourceTypeName(iconResourceId))
.appendPath(resources.getResourceEntryName(iconResourceId))
.build();

Bitmap bitmap = Glide.with(context)
Expand All @@ -524,8 +519,10 @@ private Set<String> getInstalledPackages() {
packageManager.queryIntentActivities(mainIntent, /*flags=*/ 0);
Set<String> result = new HashSet<>();
for (ResolveInfo info : pkgAppsList) {
int iconResourceId = getResourceId(info.activityInfo.packageName);
if (iconResourceId != 0) {
String packageName = info.activityInfo.packageName;
int iconResourceId = getResourceId(packageName);
if (iconResourceId != 0
&& doesApplicationPackageNameMatchResourcePackageName(packageName, iconResourceId)) {
result.add(info.activityInfo.packageName);
}
}
Expand All @@ -541,4 +538,44 @@ private int getResourceId(String packageName) {
}
return packageInfo.applicationInfo.icon;
}

/**
* Returns {@code true} iff the resource package name is exactly the same as the containing
* application package name for a given resource id.
*
* <p>The resource package name is the value returned by
* {@link Resources#getResourcePackageName(int)}. The application package name is package name of
* the enclosing application. If these two things are equal, then we can both construct a Context
* for that package and retrieve a resource id for that package from a "standard" resource Uri
* containing a name instead of an id. If they aren't equal, then we can do only one of the two
* required tasks, so our Uri load will always fail. To handle this properly, we'd need callers to
* include both package names in the Uri. I'm not aware of any standardized Uri format for doing
* so, so these requests will just be treated as unsupported for the time being.
*
* <p>Take Calendar (emulators API 24 and below) as an example:
* <ul>
* <li>package name: com.google.android.calendar</li>
* <li>resource package name: com.android.calendar</li>
* </ul>
* We can construct one of two possible Uris:
* <ul>
* <li>android.resource://com.google.android.calendar/mipmap/ic_icon_calendar.</li>
* <li>android.resource://com.android.calendar/mipmap/ic_icon_calendar.<</li>
* </ul>
* From the first Uri, we can obtain the correct Context/Resources for the calendar package, but
* our attempts to resolve the correct resource id will fail because we do not have the resource
* package name. From the second Uri we cannot obtain the Context/Resources for the calendar
* package because the resource package name doesn't match the application package name.
*/
private boolean doesApplicationPackageNameMatchResourcePackageName(
String applicationPackageName, int iconResourceId) {
try {
Context current = context.createPackageContext(applicationPackageName, /*flags=*/ 0);
String resourcePackageName = current.getResources().getResourcePackageName(iconResourceId);
return applicationPackageName.equals(resourcePackageName);
} catch (NameNotFoundException e) {
// This should never happen
throw new RuntimeException(e);
}
}
}
3 changes: 2 additions & 1 deletion library/src/main/java/com/bumptech/glide/RequestManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.bumptech.glide.manager.RequestManagerTreeNode;
import com.bumptech.glide.manager.RequestTracker;
import com.bumptech.glide.manager.TargetTracker;
import com.bumptech.glide.request.BaseRequestOptions;
import com.bumptech.glide.request.Request;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.RequestOptions;
Expand Down Expand Up @@ -153,7 +154,7 @@ private void updateRequestOptions(@NonNull RequestOptions toUpdate) {
*
* <p>The modified options will only be applied to loads started after this method is called.
*
* @see RequestBuilder#apply(com.bumptech.glide.request.BaseRequestOptions)
* @see RequestBuilder#apply(BaseRequestOptions)
*
* @return This request manager.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.support.annotation.DrawableRes;
Expand All @@ -22,6 +23,18 @@
* other packages.
*/
public class ResourceDrawableDecoder implements ResourceDecoder<Uri, Drawable> {
/**
* The package name to provide {@link Resources#getIdentifier(String, String, String)} when trying
* to find system resource ids.
*
* <p>As far as I can tell this is undocumented, but works.
*/
private static final String ANDROID_PACKAGE_NAME = "android";
/**
* {@link Resources#getIdentifier(String, String, String)} documents that it will return 0 and
* that 0 is not a valid resouce id.
*/
private static final int MISSING_RESOURCE_ID = 0;
// android.resource://<package_name>/<type>/<name>.
private static final int NAME_URI_PATH_SEGMENTS = 2;
private static final int TYPE_PATH_SEGMENT_INDEX = 0;
Expand All @@ -45,49 +58,71 @@ public boolean handles(@NonNull Uri source, @NonNull Options options) {
@Override
public Resource<Drawable> decode(@NonNull Uri source, int width, int height,
@NonNull Options options) {
@DrawableRes int resId = loadResourceIdFromUri(source);
String packageName = source.getAuthority();
Context targetContext =
packageName.equals(context.getPackageName())
? context
: getContextForPackage(source, packageName);
Context targetContext = findContextForPackage(source, packageName);
@DrawableRes int resId = findResourceIdFromUri(targetContext, source);
// We can't get a theme from another application.
Drawable drawable = DrawableDecoderCompat.getDrawable(context, targetContext, resId);
return NonOwnedDrawableResource.newInstance(drawable);
}

@NonNull
private Context getContextForPackage(Uri source, String packageName) {
private Context findContextForPackage(Uri source, String packageName) {
// Fast path
if (packageName.equals(context.getPackageName())) {
return context;
}

try {
return context.createPackageContext(packageName, /*flags=*/ 0);
} catch (NameNotFoundException e) {
// The parent APK holds the correct context if the resource is located in a split
if (packageName.contains(context.getPackageName())) {
return context;
}

throw new IllegalArgumentException(
"Failed to obtain context or unrecognized Uri format for: " + source, e);
}
}

@DrawableRes
private int loadResourceIdFromUri(Uri source) {
private int findResourceIdFromUri(Context context, Uri source) {
List<String> segments = source.getPathSegments();
@DrawableRes Integer result = null;
if (segments.size() == NAME_URI_PATH_SEGMENTS) {
String packageName = source.getAuthority();
String typeName = segments.get(TYPE_PATH_SEGMENT_INDEX);
String resourceName = segments.get(NAME_PATH_SEGMENT_INDEX);
result = context.getResources().getIdentifier(resourceName, typeName, packageName);
return findResourceIdFromTypeAndNameResourceUri(context, source);
} else if (segments.size() == ID_PATH_SEGMENTS) {
try {
result = Integer.valueOf(segments.get(RESOURCE_ID_SEGMENT_INDEX));
} catch (NumberFormatException e) {
// Ignored.
}
return findResourceIdFromResourceIdUri(source);
} else {
throw new IllegalArgumentException("Unrecognized Uri format: " + source);
}
}

if (result == null) {
throw new IllegalArgumentException("Unrecognized Uri format: " + source);
} else if (result == 0) {
throw new IllegalArgumentException("Failed to obtain resource id for: " + source);
// android.resource://com.android.camera2/mipmap/logo_camera_color
@DrawableRes
private int findResourceIdFromTypeAndNameResourceUri(Context context, Uri source) {
List<String> segments = source.getPathSegments();
String packageName = source.getAuthority();
String typeName = segments.get(TYPE_PATH_SEGMENT_INDEX);
String resourceName = segments.get(NAME_PATH_SEGMENT_INDEX);
int result = context.getResources().getIdentifier(resourceName, typeName, packageName);
if (result == MISSING_RESOURCE_ID) {
result = Resources.getSystem().getIdentifier(resourceName, typeName, ANDROID_PACKAGE_NAME);
}
if (result == MISSING_RESOURCE_ID) {
throw new IllegalArgumentException("Failed to find resource id for: " + source);
}
return result;
}

// android.resource://com.android.camera2/123456
@DrawableRes
private int findResourceIdFromResourceIdUri(Uri source) {
List<String> segments = source.getPathSegments();
try {
return Integer.parseInt(segments.get(RESOURCE_ID_SEGMENT_INDEX));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Unrecognized Uri format: " + source, e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public CustomTarget() {
}

/**
* Creates a new {@code CustomTarget} that will return the given {@code width} and {@code length}
* Creates a new {@code CustomTarget} that will return the given {@code width} and {@code height}
* as the requested size (unless overridden by
* {@link com.bumptech.glide.request.RequestOptions#override(int)} in the request).
*
Expand Down

0 comments on commit e2df4d0

Please sign in to comment.