diff options
Diffstat (limited to 'core/java/android')
20 files changed, 779 insertions, 360 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index ec2868a..296e9d3 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1114,7 +1114,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM int pid = data.readInt(); int uid = data.readInt(); int mode = data.readInt(); - int res = checkUriPermission(uri, pid, uid, mode); + int userId = data.readInt(); + int res = checkUriPermission(uri, pid, uid, mode, userId); reply.writeNoException(); reply.writeInt(res); return true; @@ -1139,7 +1140,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM String targetPkg = data.readString(); Uri uri = Uri.CREATOR.createFromParcel(data); int mode = data.readInt(); - grantUriPermission(app, targetPkg, uri, mode); + int userId = data.readInt(); + grantUriPermission(app, targetPkg, uri, mode, userId); reply.writeNoException(); return true; } @@ -1150,7 +1152,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM IApplicationThread app = ApplicationThreadNative.asInterface(b); Uri uri = Uri.CREATOR.createFromParcel(data); int mode = data.readInt(); - revokeUriPermission(app, uri, mode); + int userId = data.readInt(); + revokeUriPermission(app, uri, mode, userId); reply.writeNoException(); return true; } @@ -1159,7 +1162,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); Uri uri = Uri.CREATOR.createFromParcel(data); int mode = data.readInt(); - takePersistableUriPermission(uri, mode); + int userId = data.readInt(); + takePersistableUriPermission(uri, mode, userId); reply.writeNoException(); return true; } @@ -1168,7 +1172,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM data.enforceInterface(IActivityManager.descriptor); Uri uri = Uri.CREATOR.createFromParcel(data); int mode = data.readInt(); - releasePersistableUriPermission(uri, mode); + int userId = data.readInt(); + releasePersistableUriPermission(uri, mode, userId); reply.writeNoException(); return true; } @@ -1602,7 +1607,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM String targetPkg = data.readString(); Uri uri = Uri.CREATOR.createFromParcel(data); int mode = data.readInt(); - grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode); + int userId = data.readInt(); + grantUriPermissionFromOwner(owner, fromUid, targetPkg, uri, mode, userId); reply.writeNoException(); return true; } @@ -1612,10 +1618,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM IBinder owner = data.readStrongBinder(); Uri uri = null; if (data.readInt() != 0) { - Uri.CREATOR.createFromParcel(data); + uri = Uri.CREATOR.createFromParcel(data); } int mode = data.readInt(); - revokeUriPermissionFromOwner(owner, uri, mode); + int userId = data.readInt(); + revokeUriPermissionFromOwner(owner, uri, mode, userId); reply.writeNoException(); return true; } @@ -1626,7 +1633,8 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM String targetPkg = data.readString(); Uri uri = Uri.CREATOR.createFromParcel(data); int modeFlags = data.readInt(); - int res = checkGrantUriPermission(callingUid, targetPkg, uri, modeFlags); + int userId = data.readInt(); + int res = checkGrantUriPermission(callingUid, targetPkg, uri, modeFlags, userId); reply.writeNoException(); reply.writeInt(res); return true; @@ -3540,7 +3548,7 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return res; } - public int checkUriPermission(Uri uri, int pid, int uid, int mode) + public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -3549,6 +3557,7 @@ class ActivityManagerProxy implements IActivityManager data.writeInt(pid); data.writeInt(uid); data.writeInt(mode); + data.writeInt(userId); mRemote.transact(CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0); reply.readException(); int res = reply.readInt(); @@ -3557,7 +3566,7 @@ class ActivityManagerProxy implements IActivityManager return res; } public void grantUriPermission(IApplicationThread caller, String targetPkg, - Uri uri, int mode) throws RemoteException { + Uri uri, int mode, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); @@ -3565,19 +3574,21 @@ class ActivityManagerProxy implements IActivityManager data.writeString(targetPkg); uri.writeToParcel(data, 0); data.writeInt(mode); + data.writeInt(userId); mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); } public void revokeUriPermission(IApplicationThread caller, Uri uri, - int mode) throws RemoteException { + int mode, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(caller.asBinder()); uri.writeToParcel(data, 0); data.writeInt(mode); + data.writeInt(userId); mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); @@ -3585,12 +3596,14 @@ class ActivityManagerProxy implements IActivityManager } @Override - public void takePersistableUriPermission(Uri uri, int mode) throws RemoteException { + public void takePersistableUriPermission(Uri uri, int mode, int userId) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); uri.writeToParcel(data, 0); data.writeInt(mode); + data.writeInt(userId); mRemote.transact(TAKE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); @@ -3598,12 +3611,14 @@ class ActivityManagerProxy implements IActivityManager } @Override - public void releasePersistableUriPermission(Uri uri, int mode) throws RemoteException { + public void releasePersistableUriPermission(Uri uri, int mode, int userId) + throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); uri.writeToParcel(data, 0); data.writeInt(mode); + data.writeInt(userId); mRemote.transact(RELEASE_PERSISTABLE_URI_PERMISSION_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); @@ -4157,7 +4172,7 @@ class ActivityManagerProxy implements IActivityManager } public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg, - Uri uri, int mode) throws RemoteException { + Uri uri, int mode, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); @@ -4166,6 +4181,7 @@ class ActivityManagerProxy implements IActivityManager data.writeString(targetPkg); uri.writeToParcel(data, 0); data.writeInt(mode); + data.writeInt(userId); mRemote.transact(GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); @@ -4173,7 +4189,7 @@ class ActivityManagerProxy implements IActivityManager } public void revokeUriPermissionFromOwner(IBinder owner, Uri uri, - int mode) throws RemoteException { + int mode, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); @@ -4185,6 +4201,7 @@ class ActivityManagerProxy implements IActivityManager data.writeInt(0); } data.writeInt(mode); + data.writeInt(userId); mRemote.transact(REVOKE_URI_PERMISSION_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); @@ -4192,7 +4209,7 @@ class ActivityManagerProxy implements IActivityManager } public int checkGrantUriPermission(int callingUid, String targetPkg, - Uri uri, int modeFlags) throws RemoteException { + Uri uri, int modeFlags, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); @@ -4200,6 +4217,7 @@ class ActivityManagerProxy implements IActivityManager data.writeString(targetPkg); uri.writeToParcel(data, 0); data.writeInt(modeFlags); + data.writeInt(userId); mRemote.transact(CHECK_GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0); reply.readException(); int res = reply.readInt(); diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index a49359f..692efd7 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -365,11 +365,15 @@ public class ActivityOptions { * @param listener The listener to use to monitor activity transition events. * @return Returns a new ActivityOptions object that you can use to * supply these options as the options Bundle when starting an activity. + * Returns null if the Window does not have {@link Window#FEATURE_CONTENT_TRANSITIONS}. * @see android.transition.Transition#setEpicenterCallback( * android.transition.Transition.EpicenterCallback) */ public static ActivityOptions makeSceneTransitionAnimation(Window window, ActivityTransitionListener listener) { + if (!window.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) { + return null; + } ActivityOptions opts = new ActivityOptions(); opts.mAnimationType = ANIM_SCENE_TRANSITION; ExitTransitionCoordinator exit = new ExitTransitionCoordinator(window, listener); diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java index 3eb2fea..ca64788 100644 --- a/core/java/android/app/ActivityTransitionCoordinator.java +++ b/core/java/android/app/ActivityTransitionCoordinator.java @@ -472,7 +472,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { if (getViewsTransition() != null) { setViewVisibility(mEnteringViews, View.VISIBLE); } - getDecor().findSharedElements(map); + getDecor().findNamedViews(map); if (getViewsTransition() != null) { setViewVisibility(mEnteringViews, View.INVISIBLE); } @@ -712,7 +712,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ()); - sharedElementBundle.putString(KEY_NAME, view.getSharedElementName()); + sharedElementBundle.putString(KEY_NAME, view.getViewName()); Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); @@ -882,7 +882,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver { imageView.setId(com.android.internal.R.id.shared_element); imageView.setScaleType(ImageView.ScaleType.CENTER); imageView.setImageBitmap(bitmap); - imageView.setSharedElementName(name); + imageView.setViewName(name); setSharedElementState(imageView, name, state, parentLoc); if (mTargetSharedNames.contains(name)) { accepted.add(imageView); diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 801182d..b4d8942 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -26,6 +26,7 @@ import com.android.internal.util.Preconditions; import android.bluetooth.BluetoothManager; import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentProvider; import android.content.ContentResolver; import android.content.Context; import android.content.ContextWrapper; @@ -1818,8 +1819,8 @@ class ContextImpl extends Context { public void grantUriPermission(String toPackage, Uri uri, int modeFlags) { try { ActivityManagerNative.getDefault().grantUriPermission( - mMainThread.getApplicationThread(), toPackage, uri, - modeFlags); + mMainThread.getApplicationThread(), toPackage, + ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri)); } catch (RemoteException e) { } } @@ -1828,8 +1829,8 @@ class ContextImpl extends Context { public void revokeUriPermission(Uri uri, int modeFlags) { try { ActivityManagerNative.getDefault().revokeUriPermission( - mMainThread.getApplicationThread(), uri, - modeFlags); + mMainThread.getApplicationThread(), + ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri)); } catch (RemoteException e) { } } @@ -1838,12 +1839,17 @@ class ContextImpl extends Context { public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) { try { return ActivityManagerNative.getDefault().checkUriPermission( - uri, pid, uid, modeFlags); + ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags, + resolveUserId(uri)); } catch (RemoteException e) { return PackageManager.PERMISSION_DENIED; } } + private int resolveUserId(Uri uri) { + return ContentProvider.getUserIdFromUri(uri, getUserId()); + } + @Override public int checkCallingUriPermission(Uri uri, int modeFlags) { int pid = Binder.getCallingPid(); @@ -2280,12 +2286,16 @@ class ContextImpl extends Context { @Override protected IContentProvider acquireProvider(Context context, String auth) { - return mMainThread.acquireProvider(context, auth, mUser.getIdentifier(), true); + return mMainThread.acquireProvider(context, + ContentProvider.getAuthorityWithoutUserId(auth), + resolveUserIdFromAuthority(auth), true); } @Override protected IContentProvider acquireExistingProvider(Context context, String auth) { - return mMainThread.acquireExistingProvider(context, auth, mUser.getIdentifier(), true); + return mMainThread.acquireExistingProvider(context, + ContentProvider.getAuthorityWithoutUserId(auth), + resolveUserIdFromAuthority(auth), true); } @Override @@ -2295,7 +2305,9 @@ class ContextImpl extends Context { @Override protected IContentProvider acquireUnstableProvider(Context c, String auth) { - return mMainThread.acquireProvider(c, auth, mUser.getIdentifier(), false); + return mMainThread.acquireProvider(c, + ContentProvider.getAuthorityWithoutUserId(auth), + resolveUserIdFromAuthority(auth), false); } @Override @@ -2312,5 +2324,10 @@ class ContextImpl extends Context { public void appNotRespondingViaProvider(IContentProvider icp) { mMainThread.appNotRespondingViaProvider(icp.asBinder()); } + + /** @hide */ + protected int resolveUserIdFromAuthority(String auth) { + return ContentProvider.getUserIdFromAuthority(auth, mUser.getIdentifier()); + } } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 074b427..a30a64a 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -210,14 +210,16 @@ public interface IActivityManager extends IInterface { public int checkPermission(String permission, int pid, int uid) throws RemoteException; - public int checkUriPermission(Uri uri, int pid, int uid, int mode) + public int checkUriPermission(Uri uri, int pid, int uid, int mode, int userId) + throws RemoteException; + public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri, + int mode, int userId) throws RemoteException; + public void revokeUriPermission(IApplicationThread caller, Uri uri, int mode, int userId) + throws RemoteException; + public void takePersistableUriPermission(Uri uri, int modeFlags, int userId) + throws RemoteException; + public void releasePersistableUriPermission(Uri uri, int modeFlags, int userId) throws RemoteException; - public void grantUriPermission(IApplicationThread caller, String targetPkg, - Uri uri, int mode) throws RemoteException; - public void revokeUriPermission(IApplicationThread caller, Uri uri, - int mode) throws RemoteException; - public void takePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException; - public void releasePersistableUriPermission(Uri uri, int modeFlags) throws RemoteException; public ParceledListSlice<UriPermission> getPersistedUriPermissions( String packageName, boolean incoming) throws RemoteException; @@ -323,12 +325,12 @@ public interface IActivityManager extends IInterface { public IBinder newUriPermissionOwner(String name) throws RemoteException; public void grantUriPermissionFromOwner(IBinder owner, int fromUid, String targetPkg, - Uri uri, int mode) throws RemoteException; + Uri uri, int mode, int userId) throws RemoteException; public void revokeUriPermissionFromOwner(IBinder owner, Uri uri, - int mode) throws RemoteException; + int mode, int userId) throws RemoteException; - public int checkGrantUriPermission(int callingUid, String targetPkg, - Uri uri, int modeFlags) throws RemoteException; + public int checkGrantUriPermission(int callingUid, String targetPkg, Uri uri, + int modeFlags, int userId) throws RemoteException; // Cause the specified process to dump the specified heap. public boolean dumpHeap(String process, int userId, boolean managed, String path, diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 96c7246..9ec7f41 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -340,9 +340,11 @@ public abstract class LauncherActivity extends ListActivity { super.onCreate(icicle); mPackageManager = getPackageManager(); - - requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - setProgressBarIndeterminateVisibility(true); + + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) { + requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); + setProgressBarIndeterminateVisibility(true); + } onSetContentView(); mIconResizer = new IconResizer(); @@ -357,7 +359,9 @@ public abstract class LauncherActivity extends ListActivity { updateAlertTitle(); updateButtonText(); - setProgressBarIndeterminateVisibility(false); + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) { + setProgressBarIndeterminateVisibility(false); + } } private void updateAlertTitle() { diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java index 50c4fed..b44abf9 100644 --- a/core/java/android/content/ClipData.java +++ b/core/java/android/content/ClipData.java @@ -16,6 +16,7 @@ package android.content; +import static android.content.ContentProvider.maybeAddUserId; import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.net.Uri; @@ -186,7 +187,7 @@ public class ClipData implements Parcelable { final CharSequence mText; final String mHtmlText; final Intent mIntent; - final Uri mUri; + Uri mUri; /** * Create an Item consisting of a single block of (possibly styled) text. @@ -809,6 +810,24 @@ public class ClipData implements Parcelable { } } + /** + * Prepare this {@link ClipData} to leave an app process. + * + * @hide + */ + public void prepareToLeaveUser(int userId) { + final int size = mItems.size(); + for (int i = 0; i < size; i++) { + final Item item = mItems.get(i); + if (item.mIntent != null) { + item.mIntent.prepareToLeaveUser(userId); + } + if (item.mUri != null) { + item.mUri = maybeAddUserId(item.mUri, userId); + } + } + } + @Override public String toString() { StringBuilder b = new StringBuilder(128); diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 02c850b..be9782f 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -36,6 +36,7 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.UserHandle; import android.util.Log; +import android.text.TextUtils; import java.io.File; import java.io.FileDescriptor; @@ -195,6 +196,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { return rejectQuery(uri, projection, selection, selectionArgs, sortOrder, CancellationSignal.fromTransport(cancellationSignal)); } + uri = getUriWithoutUserId(uri); final String original = setCallingPackage(callingPkg); try { return ContentProvider.this.query( @@ -207,6 +209,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public String getType(Uri uri) { + uri = getUriWithoutUserId(uri); return ContentProvider.this.getType(uri); } @@ -215,9 +218,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 { if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { return rejectInsert(uri, initialValues); } + int userId = getUserIdFromUri(uri); + uri = getUriWithoutUserId(uri); final String original = setCallingPackage(callingPkg); try { - return ContentProvider.this.insert(uri, initialValues); + return maybeAddUserId(ContentProvider.this.insert(uri, initialValues), userId); } finally { setCallingPackage(original); } @@ -228,6 +233,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { return 0; } + uri = getUriWithoutUserId(uri); final String original = setCallingPackage(callingPkg); try { return ContentProvider.this.bulkInsert(uri, initialValues); @@ -240,24 +246,39 @@ public abstract class ContentProvider implements ComponentCallbacks2 { public ContentProviderResult[] applyBatch(String callingPkg, ArrayList<ContentProviderOperation> operations) throws OperationApplicationException { - for (ContentProviderOperation operation : operations) { + int numOperations = operations.size(); + final int[] userIds = new int[numOperations]; + for (int i = 0; i < numOperations; i++) { + ContentProviderOperation operation = operations.get(i); + userIds[i] = getUserIdFromUri(operation.getUri()); if (operation.isReadOperation()) { if (enforceReadPermission(callingPkg, operation.getUri()) != AppOpsManager.MODE_ALLOWED) { throw new OperationApplicationException("App op not allowed", 0); } } - if (operation.isWriteOperation()) { if (enforceWritePermission(callingPkg, operation.getUri()) != AppOpsManager.MODE_ALLOWED) { throw new OperationApplicationException("App op not allowed", 0); } } + if (userIds[i] != UserHandle.USER_CURRENT) { + // Removing the user id from the uri. + operation = new ContentProviderOperation(operation, true); + } + operations.set(i, operation); } final String original = setCallingPackage(callingPkg); try { - return ContentProvider.this.applyBatch(operations); + ContentProviderResult[] results = ContentProvider.this.applyBatch(operations); + for (int i = 0; i < results.length ; i++) { + if (userIds[i] != UserHandle.USER_CURRENT) { + // Adding the userId to the uri. + results[i] = new ContentProviderResult(results[i], userIds[i]); + } + } + return results; } finally { setCallingPackage(original); } @@ -268,6 +289,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { return 0; } + uri = getUriWithoutUserId(uri); final String original = setCallingPackage(callingPkg); try { return ContentProvider.this.delete(uri, selection, selectionArgs); @@ -282,6 +304,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { return 0; } + uri = getUriWithoutUserId(uri); final String original = setCallingPackage(callingPkg); try { return ContentProvider.this.update(uri, values, selection, selectionArgs); @@ -295,6 +318,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal) throws FileNotFoundException { enforceFilePermission(callingPkg, uri, mode); + uri = getUriWithoutUserId(uri); final String original = setCallingPackage(callingPkg); try { return ContentProvider.this.openFile( @@ -309,6 +333,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { String callingPkg, Uri uri, String mode, ICancellationSignal cancellationSignal) throws FileNotFoundException { enforceFilePermission(callingPkg, uri, mode); + uri = getUriWithoutUserId(uri); final String original = setCallingPackage(callingPkg); try { return ContentProvider.this.openAssetFile( @@ -330,6 +355,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { @Override public String[] getStreamTypes(Uri uri, String mimeTypeFilter) { + uri = getUriWithoutUserId(uri); return ContentProvider.this.getStreamTypes(uri, mimeTypeFilter); } @@ -337,6 +363,7 @@ public abstract class ContentProvider implements ComponentCallbacks2 { public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri uri, String mimeType, Bundle opts, ICancellationSignal cancellationSignal) throws FileNotFoundException { enforceFilePermission(callingPkg, uri, "r"); + uri = getUriWithoutUserId(uri); final String original = setCallingPackage(callingPkg); try { return ContentProvider.this.openTypedAssetFile( @@ -356,9 +383,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 { if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { return null; } + int userId = getUserIdFromUri(uri); + uri = getUriWithoutUserId(uri); final String original = setCallingPackage(callingPkg); try { - return ContentProvider.this.canonicalize(uri); + return maybeAddUserId(ContentProvider.this.canonicalize(uri), userId); } finally { setCallingPackage(original); } @@ -369,9 +398,11 @@ public abstract class ContentProvider implements ComponentCallbacks2 { if (enforceReadPermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) { return null; } + int userId = getUserIdFromUri(uri); + uri = getUriWithoutUserId(uri); final String original = setCallingPackage(callingPkg); try { - return ContentProvider.this.uncanonicalize(uri); + return maybeAddUserId(ContentProvider.this.uncanonicalize(uri), userId); } finally { setCallingPackage(original); } @@ -1680,4 +1711,75 @@ public abstract class ContentProvider implements ComponentCallbacks2 { public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { writer.println("nothing to dump"); } + + /** @hide */ + public static int getUserIdFromAuthority(String auth, int defaultUserId) { + if (auth == null) return defaultUserId; + int end = auth.indexOf('@'); + if (end == -1) return defaultUserId; + String userIdString = auth.substring(0, end); + try { + return Integer.parseInt(userIdString); + } catch (NumberFormatException e) { + Log.w(TAG, "Error parsing userId.", e); + return UserHandle.USER_NULL; + } + } + + /** @hide */ + public static int getUserIdFromAuthority(String auth) { + return getUserIdFromAuthority(auth, UserHandle.USER_CURRENT); + } + + /** @hide */ + public static int getUserIdFromUri(Uri uri, int defaultUserId) { + if (uri == null) return defaultUserId; + return getUserIdFromAuthority(uri.getAuthority(), defaultUserId); + } + + /** @hide */ + public static int getUserIdFromUri(Uri uri) { + return getUserIdFromUri(uri, UserHandle.USER_CURRENT); + } + + /** + * Removes userId part from authority string. Expects format: + * userId@some.authority + * If there is no userId in the authority, it symply returns the argument + * @hide + */ + public static String getAuthorityWithoutUserId(String auth) { + if (auth == null) return null; + int end = auth.indexOf('@'); + return auth.substring(end+1); + } + + /** @hide */ + public static Uri getUriWithoutUserId(Uri uri) { + if (uri == null) return null; + Uri.Builder builder = uri.buildUpon(); + builder.authority(getAuthorityWithoutUserId(uri.getAuthority())); + return builder.build(); + } + + /** @hide */ + public static boolean uriHasUserId(Uri uri) { + if (uri == null) return false; + return !TextUtils.isEmpty(uri.getUserInfo()); + } + + /** @hide */ + public static Uri maybeAddUserId(Uri uri, int userId) { + if (uri == null) return null; + if (userId != UserHandle.USER_CURRENT + && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { + if (!uriHasUserId(uri)) { + //We don't add the user Id if there's already one + Uri.Builder builder = uri.buildUpon(); + builder.encodedAuthority("" + userId + "@" + uri.getEncodedAuthority()); + return builder.build(); + } + } + return uri; + } } diff --git a/core/java/android/content/ContentProviderOperation.java b/core/java/android/content/ContentProviderOperation.java index 12e9bab..136e54d 100644 --- a/core/java/android/content/ContentProviderOperation.java +++ b/core/java/android/content/ContentProviderOperation.java @@ -16,6 +16,7 @@ package android.content; +import android.content.ContentProvider; import android.database.Cursor; import android.net.Uri; import android.os.Parcel; @@ -87,6 +88,31 @@ public class ContentProviderOperation implements Parcelable { mYieldAllowed = source.readInt() != 0; } + /** @hide */ + public ContentProviderOperation(ContentProviderOperation cpo, boolean removeUserIdFromUri) { + mType = cpo.mType; + if (removeUserIdFromUri) { + mUri = ContentProvider.getUriWithoutUserId(cpo.mUri); + } else { + mUri = cpo.mUri; + } + mValues = cpo.mValues; + mSelection = cpo.mSelection; + mSelectionArgs = cpo.mSelectionArgs; + mExpectedCount = cpo.mExpectedCount; + mSelectionArgsBackReferences = cpo.mSelectionArgsBackReferences; + mValuesBackReferences = cpo.mValuesBackReferences; + mYieldAllowed = cpo.mYieldAllowed; + } + + /** @hide */ + public ContentProviderOperation getWithoutUserIdInUri() { + if (ContentProvider.uriHasUserId(mUri)) { + return new ContentProviderOperation(this, true); + } + return this; + } + public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mType); Uri.writeToParcel(dest, mUri); @@ -387,7 +413,6 @@ public class ContentProviderOperation implements Parcelable { } }; - /** * Used to add parameters to a {@link ContentProviderOperation}. The {@link Builder} is * first created by calling {@link ContentProviderOperation#newInsert(android.net.Uri)}, diff --git a/core/java/android/content/ContentProviderResult.java b/core/java/android/content/ContentProviderResult.java index 5d188ef..ec3d002 100644 --- a/core/java/android/content/ContentProviderResult.java +++ b/core/java/android/content/ContentProviderResult.java @@ -16,7 +16,9 @@ package android.content; +import android.content.ContentProvider; import android.net.Uri; +import android.os.UserHandle; import android.os.Parcelable; import android.os.Parcel; @@ -50,6 +52,12 @@ public class ContentProviderResult implements Parcelable { } } + /** @hide */ + public ContentProviderResult(ContentProviderResult cpr, int userId) { + uri = ContentProvider.maybeAddUserId(cpr.uri, userId); + count = cpr.count; + } + public void writeToParcel(Parcel dest, int flags) { if (uri == null) { dest.writeInt(1); @@ -81,4 +89,4 @@ public class ContentProviderResult implements Parcelable { } return "ContentProviderResult(count=" + count + ")"; } -}
\ No newline at end of file +} diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 5b41394..7642e13 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -1643,7 +1643,8 @@ public abstract class ContentResolver { */ public void takePersistableUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags) { try { - ActivityManagerNative.getDefault().takePersistableUriPermission(uri, modeFlags); + ActivityManagerNative.getDefault().takePersistableUriPermission( + ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri)); } catch (RemoteException e) { } } @@ -1658,7 +1659,8 @@ public abstract class ContentResolver { */ public void releasePersistableUriPermission(Uri uri, @Intent.AccessUriMode int modeFlags) { try { - ActivityManagerNative.getDefault().releasePersistableUriPermission(uri, modeFlags); + ActivityManagerNative.getDefault().releasePersistableUriPermission( + ContentProvider.getUriWithoutUserId(uri), modeFlags, resolveUserId(uri)); } catch (RemoteException e) { } } @@ -2462,4 +2464,9 @@ public abstract class ContentResolver { private final Context mContext; final String mPackageName; private static final String TAG = "ContentResolver"; + + /** @hide */ + public int resolveUserId(Uri uri) { + return ContentProvider.getUserIdFromUri(uri, mContext.getUserId()); + } } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 3cfc56c..076f657 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -26,6 +26,7 @@ import android.annotation.IntDef; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.pm.ActivityInfo; +import static android.content.ContentProvider.maybeAddUserId; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; @@ -7433,6 +7434,41 @@ public class Intent implements Parcelable, Cloneable { } /** + * Prepare this {@link Intent} to be sent to another user + * + * @hide + */ + public void prepareToLeaveUser(int userId) { + Uri data = getData(); + if (data != null) { + mData = maybeAddUserId(data, userId); + } + if (mSelector != null) { + mSelector.prepareToLeaveUser(userId); + } + if (mClipData != null) { + mClipData.prepareToLeaveUser(userId); + } + String action = getAction(); + if (ACTION_SEND.equals(action)) { + final Uri stream = getParcelableExtra(EXTRA_STREAM); + if (stream != null) { + putExtra(EXTRA_STREAM, maybeAddUserId(stream, userId)); + } + } + if (ACTION_SEND_MULTIPLE.equals(action)) { + final ArrayList<Uri> streams = getParcelableArrayListExtra(EXTRA_STREAM); + if (streams != null) { + ArrayList<Uri> newStreams = new ArrayList<Uri>(); + for (int i = 0; i < streams.size(); i++) { + newStreams.add(maybeAddUserId(streams.get(i), userId)); + } + putParcelableArrayListExtra(EXTRA_STREAM, newStreams); + } + } + } + + /** * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested * intents in {@link #ACTION_CHOOSER}. diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java index 49a0138..5a432dc 100644 --- a/core/java/android/transition/Transition.java +++ b/core/java/android/transition/Transition.java @@ -24,6 +24,7 @@ import android.util.ArrayMap; import android.util.Log; import android.util.LongSparseArray; import android.util.SparseArray; +import android.util.SparseIntArray; import android.util.SparseLongArray; import android.view.SurfaceView; import android.view.TextureView; @@ -89,7 +90,8 @@ import java.util.List; * out-in behavior. Finally, note the use of the <code>targets</code> sub-tag, which * takes a set of {@link android.R.styleable#TransitionTarget target} tags, each * of which lists a specific <code>targetId</code>, <code>targetClass</code>, - * <code>excludeId</code>, or <code>excludeClass</code>, which this transition acts upon. + * <code>targetViewName</code>, <code>excludeId</code>, <code>excludeClass</code>, or + * <code>excludeViewName</code>, which this transition acts upon. * Use of targets is optional, but can be used to either limit the time spent checking * attributes on unchanging views, or limiting the types of animations run on specific views. * In this case, we know that only the <code>grayscaleContainer</code> will be @@ -113,10 +115,12 @@ public abstract class Transition implements Cloneable { TimeInterpolator mInterpolator = null; ArrayList<Integer> mTargetIds = new ArrayList<Integer>(); ArrayList<View> mTargets = new ArrayList<View>(); + ArrayList<String> mTargetNames = null; + ArrayList<Class> mTargetTypes = null; ArrayList<Integer> mTargetIdExcludes = null; ArrayList<View> mTargetExcludes = null; ArrayList<Class> mTargetTypeExcludes = null; - ArrayList<Class> mTargetTypes = null; + ArrayList<String> mTargetNameExcludes = null; ArrayList<Integer> mTargetIdChildExcludes = null; ArrayList<View> mTargetChildExcludes = null; ArrayList<Class> mTargetTypeChildExcludes = null; @@ -334,6 +338,133 @@ public abstract class Transition implements Cloneable { } /** + * Match start/end values by View instance. Adds matched values to startValuesList + * and endValuesList and removes them from unmatchedStart and unmatchedEnd. + */ + private void matchInstances(ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList, + ArrayMap<View, TransitionValues> unmatchedStart, + ArrayMap<View, TransitionValues> unmatchedEnd) { + for (int i = unmatchedStart.size() - 1; i >= 0; i--) { + View view = unmatchedStart.keyAt(i); + TransitionValues end = unmatchedEnd.remove(view); + if (end != null) { + TransitionValues start = unmatchedStart.removeAt(i); + startValuesList.add(start); + endValuesList.add(end); + } + } + } + + /** + * Match start/end values by Adapter item ID. Adds matched values to startValuesList + * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using + * startItemIds and endItemIds as a guide for which Views have unique item IDs. + */ + private void matchItemIds(ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList, + ArrayMap<View, TransitionValues> unmatchedStart, + ArrayMap<View, TransitionValues> unmatchedEnd, + LongSparseArray<View> startItemIds, LongSparseArray<View> endItemIds) { + int numStartIds = startItemIds.size(); + for (int i = 0; i < numStartIds; i++) { + View startView = startItemIds.valueAt(i); + if (startView != null) { + View endView = endItemIds.get(startItemIds.keyAt(i)); + if (endView != null) { + TransitionValues startValues = unmatchedStart.get(startView); + TransitionValues endValues = unmatchedEnd.get(endView); + if (startValues != null && endValues != null) { + startValuesList.add(startValues); + endValuesList.add(endValues); + unmatchedStart.remove(startView); + unmatchedEnd.remove(endView); + } + } + } + } + } + + /** + * Match start/end values by Adapter view ID. Adds matched values to startValuesList + * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using + * startIds and endIds as a guide for which Views have unique IDs. + */ + private void matchIds(ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList, + ArrayMap<View, TransitionValues> unmatchedStart, + ArrayMap<View, TransitionValues> unmatchedEnd, + SparseArray<View> startIds, SparseArray<View> endIds) { + int numStartIds = startIds.size(); + for (int i = 0; i < numStartIds; i++) { + View startView = startIds.valueAt(i); + if (startView != null && isValidTarget(startView)) { + View endView = endIds.get(startIds.keyAt(i)); + if (endView != null && isValidTarget(endView)) { + TransitionValues startValues = unmatchedStart.get(startView); + TransitionValues endValues = unmatchedEnd.get(endView); + if (startValues != null && endValues != null) { + startValuesList.add(startValues); + endValuesList.add(endValues); + unmatchedStart.remove(startView); + unmatchedEnd.remove(endView); + } + } + } + } + } + + /** + * Match start/end values by Adapter viewName. Adds matched values to startValuesList + * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using + * startNames and endNames as a guide for which Views have unique viewNames. + */ + private void matchNames(ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList, + ArrayMap<View, TransitionValues> unmatchedStart, + ArrayMap<View, TransitionValues> unmatchedEnd, + ArrayMap<String, View> startNames, ArrayMap<String, View> endNames) { + int numStartNames = startNames.size(); + for (int i = 0; i < numStartNames; i++) { + View startView = startNames.valueAt(i); + if (startView != null && isValidTarget(startView)) { + View endView = endNames.get(startNames.keyAt(i)); + if (endView != null && isValidTarget(endView)) { + TransitionValues startValues = unmatchedStart.get(startView); + TransitionValues endValues = unmatchedEnd.get(endView); + if (startValues != null && endValues != null) { + startValuesList.add(startValues); + endValuesList.add(endValues); + unmatchedStart.remove(startView); + unmatchedEnd.remove(endView); + } + } + } + } + } + + /** + * Adds all values from unmatchedStart and unmatchedEnd to startValuesList and endValuesList, + * assuming that there is no match between values in the list. + */ + private void addUnmatched(ArrayList<TransitionValues> startValuesList, + ArrayList<TransitionValues> endValuesList, + ArrayMap<View, TransitionValues> unmatchedStart, + ArrayMap<View, TransitionValues> unmatchedEnd) { + // Views that only exist in the start Scene + for (int i = 0; i < unmatchedStart.size(); i++) { + startValuesList.add(unmatchedStart.valueAt(i)); + endValuesList.add(null); + } + + // Views that only exist in the end Scene + for (int i = 0; i < unmatchedEnd.size(); i++) { + endValuesList.add(unmatchedEnd.valueAt(i)); + startValuesList.add(null); + } + } + + /** * This method, essentially a wrapper around all calls to createAnimator for all * possible target views, is called with the entire set of start/end * values. The implementation in Transition iterates through these lists @@ -349,76 +480,22 @@ public abstract class Transition implements Cloneable { if (DBG) { Log.d(LOG_TAG, "createAnimators() for " + this); } - ArrayMap<View, TransitionValues> endCopy = + ArrayMap<View, TransitionValues> unmatchedStart = + new ArrayMap<View, TransitionValues>(startValues.viewValues); + ArrayMap<View, TransitionValues> unmatchedEnd = new ArrayMap<View, TransitionValues>(endValues.viewValues); - SparseArray<TransitionValues> endIdCopy = endValues.idValues.clone(); - LongSparseArray<TransitionValues> endItemIdCopy = endValues.itemIdValues.clone(); - // Walk through the start values, playing everything we find - // Remove from the end set as we go + ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>(); ArrayList<TransitionValues> endValuesList = new ArrayList<TransitionValues>(); - for (View view : startValues.viewValues.keySet()) { - TransitionValues start = null; - TransitionValues end = null; - boolean isInListView = false; - if (view.getParent() instanceof ListView) { - isInListView = true; - } - if (!isInListView) { - int id = view.getId(); - start = startValues.viewValues.get(view); - end = endValues.viewValues.get(view); - if (end != null) { - endCopy.remove(view); - } else if (id != View.NO_ID) { - end = endIdCopy.get(id); - if (end == null || startValues.viewValues.containsKey(end.view)) { - end = null; - id = View.NO_ID; - } else { - endCopy.remove(end.view); - } - } - endIdCopy.remove(id); - if (isValidTarget(view, id)) { - startValuesList.add(start); - endValuesList.add(end); - } - } else { - ListView parent = (ListView) view.getParent(); - if (parent.getAdapter().hasStableIds()) { - int position = parent.getPositionForView(view); - long itemId = parent.getItemIdAtPosition(position); - start = startValues.itemIdValues.get(itemId); - endItemIdCopy.remove(itemId); - // TODO: deal with targetIDs for itemIDs for ListView items - startValuesList.add(start); - endValuesList.add(end); - } - } - } - int startItemIdCopySize = startValues.itemIdValues.size(); - for (int i = 0; i < startItemIdCopySize; ++i) { - long id = startValues.itemIdValues.keyAt(i); - if (isValidTarget(null, id)) { - TransitionValues start = startValues.itemIdValues.get(id); - TransitionValues end = endValues.itemIdValues.get(id); - endItemIdCopy.remove(id); - startValuesList.add(start); - endValuesList.add(end); - } - } - // Now walk through the remains of the end set - // We've already matched everything from start to end, everything else doesn't match. - for (View view : endCopy.keySet()) { - int id = view.getId(); - if (isValidTarget(view, id)) { - TransitionValues start = null; - TransitionValues end = endCopy.get(view); - startValuesList.add(start); - endValuesList.add(end); - } - } + matchNames(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, + startValues.nameValues, endValues.nameValues); + matchInstances(startValuesList, endValuesList, unmatchedStart, unmatchedEnd); + matchIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, + startValues.idValues, endValues.idValues); + matchItemIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd, + startValues.itemIdValues, endValues.itemIdValues); + addUnmatched(startValuesList, endValuesList, unmatchedStart, unmatchedEnd); + ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators(); long minStartDelay = Long.MAX_VALUE; int minAnimator = mAnimators.size(); @@ -520,7 +597,8 @@ public abstract class Transition implements Cloneable { * is not checked (this is in the case of ListView items, where the * views are ignored and only the ids are used). */ - boolean isValidTarget(View target, long targetId) { + boolean isValidTarget(View target) { + int targetId = target.getId(); if (mTargetIdExcludes != null && mTargetIdExcludes.contains(targetId)) { return false; } @@ -536,10 +614,20 @@ public abstract class Transition implements Cloneable { } } } - if (mTargetIds.size() == 0 && mTargets.size() == 0 && mTargetTypes == null) { + if (mTargetNameExcludes != null && target != null && target.getViewName() != null) { + if (mTargetNameExcludes.contains(target.getViewName())) { + return false; + } + } + if (mTargetIds.size() == 0 && mTargets.size() == 0 && + (mTargetTypes == null || mTargetTypes.isEmpty() && + (mTargetNames == null || mTargetNames.isEmpty()))) { + return true; + } + if (mTargetIds.contains(targetId) || mTargets.contains(target)) { return true; } - if (mTargetIds.contains((int) targetId) || mTargets.contains(target)) { + if (mTargetNames != null && mTargetNames.contains(target.getViewName())) { return true; } if (mTargetTypes != null) { @@ -690,6 +778,33 @@ public abstract class Transition implements Cloneable { } /** + * Adds the viewName of a target view that this Transition is interested in + * animating. By default, there are no targetNames, and a Transition will + * listen for changes on every view in the hierarchy below the sceneRoot + * of the Scene being transitioned into. Setting targetNames constrains + * the Transition to only listen for, and act on, views with these viewNames. + * Views with different viewNames, or no viewName whatsoever, will be ignored. + * + * <p>Note that viewNames should be unique within the view hierarchy.</p> + * + * @see android.view.View#getViewName() + * @param targetName The viewName of a target view, must be non-null. + * @return The Transition to which the target viewName is added. + * Returning the same object makes it easier to chain calls during + * construction, such as + * <code>transitionSet.addTransitions(new Fade()).addTarget(someName);</code> + */ + public Transition addTarget(String targetName) { + if (targetName != null) { + if (mTargetNames != null) { + mTargetNames = new ArrayList<String>(); + } + mTargetNames.add(targetName); + } + return this; + } + + /** * Adds the Class of a target view that this Transition is interested in * animating. By default, there are no targetTypes, and a Transition will * listen for changes on every view in the hierarchy below the sceneRoot @@ -712,10 +827,12 @@ public abstract class Transition implements Cloneable { * <code>transitionSet.addTransitions(new Fade()).addTarget(ImageView.class);</code> */ public Transition addTarget(Class targetType) { - if (mTargetTypes == null) { - mTargetTypes = new ArrayList<Class>(); + if (targetType != null) { + if (mTargetTypes == null) { + mTargetTypes = new ArrayList<Class>(); + } + mTargetTypes.add(targetType); } - mTargetTypes.add(targetType); return this; } @@ -737,6 +854,23 @@ public abstract class Transition implements Cloneable { } /** + * Removes the given targetName from the list of viewNames that this Transition + * is interested in animating. + * + * @param targetName The viewName of a target view, must not be null. + * @return The Transition from which the targetName is removed. + * Returning the same object makes it easier to chain calls during + * construction, such as + * <code>transitionSet.addTransitions(new Fade()).removeTargetName(someName);</code> + */ + public Transition removeTarget(String targetName) { + if (targetName != null && mTargetNames != null) { + mTargetNames.remove(targetName); + } + return this; + } + + /** * Whether to add the given id to the list of target ids to exclude from this * transition. The <code>exclude</code> parameter specifies whether the target * should be added to or removed from the excluded list. @@ -758,7 +892,35 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeTarget(int targetId, boolean exclude) { - mTargetIdExcludes = excludeId(mTargetIdExcludes, targetId, exclude); + if (targetId >= 0) { + mTargetIdExcludes = excludeObject(mTargetIdExcludes, targetId, exclude); + } + return this; + } + + /** + * Whether to add the given viewName to the list of target viewNames to exclude from this + * transition. The <code>exclude</code> parameter specifies whether the target + * should be added to or removed from the excluded list. + * + * <p>Excluding targets is a general mechanism for allowing transitions to run on + * a view hierarchy while skipping target views that should not be part of + * the transition. For example, you may want to avoid animating children + * of a specific ListView or Spinner. Views can be excluded by their + * id, their instance reference, their viewName, or by the Class of that view + * (eg, {@link Spinner}).</p> + * + * @see #excludeTarget(View, boolean) + * @see #excludeTarget(int, boolean) + * @see #excludeTarget(Class, boolean) + * + * @param targetViewName The name of a target to ignore when running this transition. + * @param exclude Whether to add the target to or remove the target from the + * current list of excluded targets. + * @return This transition object. + */ + public Transition excludeTarget(String targetViewName, boolean exclude) { + mTargetNameExcludes = excludeObject(mTargetNameExcludes, targetViewName, exclude); return this; } @@ -788,23 +950,10 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeChildren(int targetId, boolean exclude) { - mTargetIdChildExcludes = excludeId(mTargetIdChildExcludes, targetId, exclude); - return this; - } - - /** - * Utility method to manage the boilerplate code that is the same whether we - * are excluding targets or their children. - */ - private ArrayList<Integer> excludeId(ArrayList<Integer> list, int targetId, boolean exclude) { - if (targetId > 0) { - if (exclude) { - list = ArrayListManager.add(list, targetId); - } else { - list = ArrayListManager.remove(list, targetId); - } + if (targetId >= 0) { + mTargetIdChildExcludes = excludeObject(mTargetIdChildExcludes, targetId, exclude); } - return list; + return this; } /** @@ -829,7 +978,7 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeTarget(View target, boolean exclude) { - mTargetExcludes = excludeView(mTargetExcludes, target, exclude); + mTargetExcludes = excludeObject(mTargetExcludes, target, exclude); return this; } @@ -855,7 +1004,7 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeChildren(View target, boolean exclude) { - mTargetChildExcludes = excludeView(mTargetChildExcludes, target, exclude); + mTargetChildExcludes = excludeObject(mTargetChildExcludes, target, exclude); return this; } @@ -863,7 +1012,7 @@ public abstract class Transition implements Cloneable { * Utility method to manage the boilerplate code that is the same whether we * are excluding targets or their children. */ - private ArrayList<View> excludeView(ArrayList<View> list, View target, boolean exclude) { + private static <T> ArrayList<T> excludeObject(ArrayList<T> list, T target, boolean exclude) { if (target != null) { if (exclude) { list = ArrayListManager.add(list, target); @@ -896,7 +1045,7 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeTarget(Class type, boolean exclude) { - mTargetTypeExcludes = excludeType(mTargetTypeExcludes, type, exclude); + mTargetTypeExcludes = excludeObject(mTargetTypeExcludes, type, exclude); return this; } @@ -923,26 +1072,11 @@ public abstract class Transition implements Cloneable { * @return This transition object. */ public Transition excludeChildren(Class type, boolean exclude) { - mTargetTypeChildExcludes = excludeType(mTargetTypeChildExcludes, type, exclude); + mTargetTypeChildExcludes = excludeObject(mTargetTypeChildExcludes, type, exclude); return this; } /** - * Utility method to manage the boilerplate code that is the same whether we - * are excluding targets or their children. - */ - private ArrayList<Class> excludeType(ArrayList<Class> list, Class type, boolean exclude) { - if (type != null) { - if (exclude) { - list = ArrayListManager.add(list, type); - } else { - list = ArrayListManager.remove(list, type); - } - } - return list; - } - - /** * Sets the target view instances that this Transition is interested in * animating. By default, there are no targets, and a Transition will * listen for changes on every view in the hierarchy below the sceneRoot @@ -991,9 +1125,27 @@ public abstract class Transition implements Cloneable { } /** - * Returns the array of target IDs that this transition limits itself to - * tracking and animating. If the array is null for both this method and - * {@link #getTargets()}, then this transition is + * Removes the given target from the list of targets that this Transition + * is interested in animating. + * + * @param target The type of the target view, must be non-null. + * @return Transition The Transition from which the target is removed. + * Returning the same object makes it easier to chain calls during + * construction, such as + * <code>transitionSet.addTransitions(new Fade()).removeTarget(someType);</code> + */ + public Transition removeTarget(Class target) { + if (target != null) { + mTargetTypes.remove(target); + } + return this; + } + + /** + * Returns the list of target IDs that this transition limits itself to + * tracking and animating. If the list is null or empty for + * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and + * {@link #getTargetTypes()} then this transition is * not limited to specific views, and will handle changes to any views * in the hierarchy of a scene change. * @@ -1004,9 +1156,10 @@ public abstract class Transition implements Cloneable { } /** - * Returns the array of target views that this transition limits itself to - * tracking and animating. If the array is null for both this method and - * {@link #getTargetIds()}, then this transition is + * Returns the list of target views that this transition limits itself to + * tracking and animating. If the list is null or empty for + * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and + * {@link #getTargetTypes()} then this transition is * not limited to specific views, and will handle changes to any views * in the hierarchy of a scene change. * @@ -1017,6 +1170,34 @@ public abstract class Transition implements Cloneable { } /** + * Returns the list of target viewNames that this transition limits itself to + * tracking and animating. If the list is null or empty for + * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and + * {@link #getTargetTypes()} then this transition is + * not limited to specific views, and will handle changes to any views + * in the hierarchy of a scene change. + * + * @return the list of target viewNames + */ + public List<String> getTargetViewNames() { + return mTargetNames; + } + + /** + * Returns the list of target viewNames that this transition limits itself to + * tracking and animating. If the list is null or empty for + * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and + * {@link #getTargetTypes()} then this transition is + * not limited to specific views, and will handle changes to any views + * in the hierarchy of a scene change. + * + * @return the list of target Types + */ + public List<Class> getTargetTypes() { + return mTargetTypes; + } + + /** * Recursive method that captures values for the given view and the * hierarchy underneath it. * @param sceneRoot The root of the view hierarchy being captured @@ -1025,52 +1206,42 @@ public abstract class Transition implements Cloneable { */ void captureValues(ViewGroup sceneRoot, boolean start) { clearValues(start); - if (mTargetIds.size() > 0 || mTargets.size() > 0) { - if (mTargetIds.size() > 0) { - for (int i = 0; i < mTargetIds.size(); ++i) { - int id = mTargetIds.get(i); - View view = sceneRoot.findViewById(id); - if (view != null) { - TransitionValues values = new TransitionValues(); - values.view = view; - if (start) { - captureStartValues(values); - } else { - captureEndValues(values); - } - capturePropagationValues(values); - if (start) { - mStartValues.viewValues.put(view, values); - if (id >= 0) { - mStartValues.idValues.put(id, values); - } - } else { - mEndValues.viewValues.put(view, values); - if (id >= 0) { - mEndValues.idValues.put(id, values); - } - } + if ((mTargetIds.size() > 0 || mTargets.size() > 0) + && (mTargetNames == null || mTargetNames.isEmpty()) + && (mTargetTypes == null || mTargetTypes.isEmpty())) { + for (int i = 0; i < mTargetIds.size(); ++i) { + int id = mTargetIds.get(i); + View view = sceneRoot.findViewById(id); + if (view != null) { + TransitionValues values = new TransitionValues(); + values.view = view; + if (start) { + captureStartValues(values); + } else { + captureEndValues(values); + } + capturePropagationValues(values); + if (start) { + addViewValues(mStartValues, view, values); + } else { + addViewValues(mEndValues, view, values); } } } - if (mTargets.size() > 0) { - for (int i = 0; i < mTargets.size(); ++i) { - View view = mTargets.get(i); - if (view != null) { - TransitionValues values = new TransitionValues(); - values.view = view; - if (start) { - captureStartValues(values); - } else { - captureEndValues(values); - } - capturePropagationValues(values); - if (start) { - mStartValues.viewValues.put(view, values); - } else { - mEndValues.viewValues.put(view, values); - } - } + for (int i = 0; i < mTargets.size(); ++i) { + View view = mTargets.get(i); + TransitionValues values = new TransitionValues(); + values.view = view; + if (start) { + captureStartValues(values); + } else { + captureEndValues(values); + } + capturePropagationValues(values); + if (start) { + mStartValues.viewValues.put(view, values); + } else { + mEndValues.viewValues.put(view, values); } } } else { @@ -1078,6 +1249,47 @@ public abstract class Transition implements Cloneable { } } + static void addViewValues(TransitionValuesMaps transitionValuesMaps, + View view, TransitionValues transitionValues) { + transitionValuesMaps.viewValues.put(view, transitionValues); + int id = view.getId(); + if (id >= 0) { + if (transitionValuesMaps.idValues.indexOfKey(id) >= 0) { + // Duplicate IDs cannot match by ID. + transitionValuesMaps.idValues.put(id, null); + } else { + transitionValuesMaps.idValues.put(id, view); + } + } + String name = view.getViewName(); + if (name != null) { + if (transitionValuesMaps.nameValues.containsKey(name)) { + // Duplicate viewNames: cannot match by viewName. + transitionValuesMaps.nameValues.put(name, null); + } else { + transitionValuesMaps.nameValues.put(name, view); + } + } + if (view.getParent() instanceof ListView) { + ListView listview = (ListView) view.getParent(); + if (listview.getAdapter().hasStableIds()) { + int position = listview.getPositionForView(view); + long itemId = listview.getItemIdAtPosition(position); + if (transitionValuesMaps.itemIdValues.indexOfKey(itemId) >= 0) { + // Duplicate item IDs: cannot match by item ID. + View alreadyMatched = transitionValuesMaps.itemIdValues.get(itemId); + if (alreadyMatched != null) { + alreadyMatched.setHasTransientState(false); + transitionValuesMaps.itemIdValues.put(itemId, null); + } + } else { + view.setHasTransientState(true); + transitionValuesMaps.itemIdValues.put(itemId, view); + } + } + } + } + /** * Clear valuesMaps for specified start/end state * @@ -1109,24 +1321,7 @@ public abstract class Transition implements Cloneable { if (view == null) { return; } - boolean isListViewItem = false; - if (view.getParent() instanceof ListView) { - isListViewItem = true; - } - if (isListViewItem && !((ListView) view.getParent()).getAdapter().hasStableIds()) { - // ignore listview children unless we can track them with stable IDs - return; - } - int id = View.NO_ID; - long itemId = View.NO_ID; - if (!isListViewItem) { - id = view.getId(); - } else { - ListView listview = (ListView) view.getParent(); - int position = listview.getPositionForView(view); - itemId = listview.getItemIdAtPosition(position); - view.setHasTransientState(true); - } + int id = view.getId(); if (mTargetIdExcludes != null && mTargetIdExcludes.contains(id)) { return; } @@ -1151,23 +1346,9 @@ public abstract class Transition implements Cloneable { } capturePropagationValues(values); if (start) { - if (!isListViewItem) { - mStartValues.viewValues.put(view, values); - if (id >= 0) { - mStartValues.idValues.put((int) id, values); - } - } else { - mStartValues.itemIdValues.put(itemId, values); - } + addViewValues(mStartValues, view, values); } else { - if (!isListViewItem) { - mEndValues.viewValues.put(view, values); - if (id >= 0) { - mEndValues.idValues.put((int) id, values); - } - } else { - mEndValues.itemIdValues.put(itemId, values); - } + addViewValues(mEndValues, view, values); } } if (view instanceof ViewGroup) { @@ -1178,7 +1359,7 @@ public abstract class Transition implements Cloneable { if (mTargetChildExcludes != null && mTargetChildExcludes.contains(view)) { return; } - if (mTargetTypeChildExcludes != null && view != null) { + if (mTargetTypeChildExcludes != null) { int numTypes = mTargetTypeChildExcludes.size(); for (int i = 0; i < numTypes; ++i) { if (mTargetTypeChildExcludes.get(i).isInstance(view)) { @@ -1204,22 +1385,7 @@ public abstract class Transition implements Cloneable { return mParent.getTransitionValues(view, start); } TransitionValuesMaps valuesMaps = start ? mStartValues : mEndValues; - TransitionValues values = valuesMaps.viewValues.get(view); - if (values == null) { - int id = view.getId(); - if (id >= 0) { - values = valuesMaps.idValues.get(id); - } - if (values == null && view.getParent() instanceof ListView) { - ListView listview = (ListView) view.getParent(); - int position = listview.getPositionForView(view); - long itemId = listview.getItemIdAtPosition(position); - values = valuesMaps.itemIdValues.get(itemId); - } - // TODO: Doesn't handle the case where a view was parented to a - // ListView (with an itemId), but no longer is - } - return values; + return valuesMaps.viewValues.get(view); } /** @@ -1303,11 +1469,7 @@ public abstract class Transition implements Cloneable { boolean cancel = false; TransitionValues oldValues = oldInfo.values; View oldView = oldInfo.view; - TransitionValues newValues = mEndValues.viewValues != null ? - mEndValues.viewValues.get(oldView) : null; - if (newValues == null) { - newValues = mEndValues.idValues.get(oldView.getId()); - } + TransitionValues newValues = mEndValues.viewValues.get(oldView); if (oldValues != null) { // if oldValues null, then transition didn't care to stash values, // and won't get canceled @@ -1429,17 +1591,15 @@ public abstract class Transition implements Cloneable { } } for (int i = 0; i < mStartValues.itemIdValues.size(); ++i) { - TransitionValues tv = mStartValues.itemIdValues.valueAt(i); - View v = tv.view; - if (v.hasTransientState()) { - v.setHasTransientState(false); + View view = mStartValues.itemIdValues.valueAt(i); + if (view != null) { + view.setHasTransientState(false); } } for (int i = 0; i < mEndValues.itemIdValues.size(); ++i) { - TransitionValues tv = mEndValues.itemIdValues.valueAt(i); - View v = tv.view; - if (v.hasTransientState()) { - v.setHasTransientState(false); + View view = mEndValues.itemIdValues.valueAt(i); + if (view != null) { + view.setHasTransientState(false); } } mEnded = true; diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java index a5e960a..04f8672 100644 --- a/core/java/android/transition/TransitionInflater.java +++ b/core/java/android/transition/TransitionInflater.java @@ -229,11 +229,20 @@ public class TransitionInflater { com.android.internal.R.styleable.TransitionTarget); int id = a.getResourceId( com.android.internal.R.styleable.TransitionTarget_targetId, -1); + String viewName; if (id >= 0) { transition.addTarget(id); } else if ((id = a.getResourceId( com.android.internal.R.styleable.TransitionTarget_excludeId, -1)) >= 0) { transition.excludeTarget(id, true); + } else if ((viewName = a.getString( + com.android.internal.R.styleable.TransitionTarget_targetViewName)) + != null) { + transition.addTarget(viewName); + } else if ((viewName = a.getString( + com.android.internal.R.styleable.TransitionTarget_excludeViewName)) + != null) { + transition.excludeTarget(viewName, true); } else { String className = a.getString( com.android.internal.R.styleable.TransitionTarget_excludeClass); diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java index 9081234..698b563 100644 --- a/core/java/android/transition/TransitionSet.java +++ b/core/java/android/transition/TransitionSet.java @@ -272,24 +272,8 @@ public class TransitionSet extends Transition { int numValues = values.viewValues.size(); for (int i = 0; i < numValues; i++) { View view = values.viewValues.keyAt(i); - if (isValidTarget(view, view.getId())) { - included.viewValues.put(view, values.viewValues.valueAt(i)); - } - } - numValues = values.idValues.size(); - for (int i = 0; i < numValues; i++) { - int id = values.idValues.keyAt(i); - TransitionValues transitionValues = values.idValues.valueAt(i); - if (isValidTarget(transitionValues.view, id)) { - included.idValues.put(id, transitionValues); - } - } - numValues = values.itemIdValues.size(); - for (int i = 0; i < numValues; i++) { - long id = values.itemIdValues.keyAt(i); - TransitionValues transitionValues = values.itemIdValues.valueAt(i); - if (isValidTarget(transitionValues.view, id)) { - included.itemIdValues.put(id, transitionValues); + if (isValidTarget(view)) { + addViewValues(included, view, values.viewValues.valueAt(i)); } } return included; @@ -328,10 +312,9 @@ public class TransitionSet extends Transition { @Override public void captureStartValues(TransitionValues transitionValues) { - int targetId = transitionValues.view.getId(); - if (isValidTarget(transitionValues.view, targetId)) { + if (isValidTarget(transitionValues.view)) { for (Transition childTransition : mTransitions) { - if (childTransition.isValidTarget(transitionValues.view, targetId)) { + if (childTransition.isValidTarget(transitionValues.view)) { childTransition.captureStartValues(transitionValues); } } @@ -340,10 +323,9 @@ public class TransitionSet extends Transition { @Override public void captureEndValues(TransitionValues transitionValues) { - int targetId = transitionValues.view.getId(); - if (isValidTarget(transitionValues.view, targetId)) { + if (isValidTarget(transitionValues.view)) { for (Transition childTransition : mTransitions) { - if (childTransition.isValidTarget(transitionValues.view, targetId)) { + if (childTransition.isValidTarget(transitionValues.view)) { childTransition.captureEndValues(transitionValues); } } diff --git a/core/java/android/transition/TransitionValuesMaps.java b/core/java/android/transition/TransitionValuesMaps.java index 131596b..6d5700a 100644 --- a/core/java/android/transition/TransitionValuesMaps.java +++ b/core/java/android/transition/TransitionValuesMaps.java @@ -24,7 +24,7 @@ import android.view.View; class TransitionValuesMaps { ArrayMap<View, TransitionValues> viewValues = new ArrayMap<View, TransitionValues>(); - SparseArray<TransitionValues> idValues = new SparseArray<TransitionValues>(); - LongSparseArray<TransitionValues> itemIdValues = - new LongSparseArray<TransitionValues>(); + SparseArray<View> idValues = new SparseArray<View>(); + LongSparseArray<View> itemIdValues = new LongSparseArray<View>(); + ArrayMap<String, View> nameValues = new ArrayMap<String, View>(); } diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java index 6e6496c..0f7638b 100644 --- a/core/java/android/transition/Visibility.java +++ b/core/java/android/transition/Visibility.java @@ -162,26 +162,15 @@ public abstract class Visibility extends Transition { public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues, TransitionValues endValues) { VisibilityInfo visInfo = getVisibilityChangeInfo(startValues, endValues); - if (visInfo.visibilityChange) { - // Only transition views that are either targets of this transition - // or whose parent hierarchies remain stable between scenes - boolean isTarget = false; - if (mTargets.size() > 0 || mTargetIds.size() > 0) { - View startView = startValues != null ? startValues.view : null; - View endView = endValues != null ? endValues.view : null; - int startId = startView != null ? startView.getId() : -1; - int endId = endView != null ? endView.getId() : -1; - isTarget = isValidTarget(startView, startId) || isValidTarget(endView, endId); - } - if (isTarget || ((visInfo.startParent != null || visInfo.endParent != null))) { - if (visInfo.fadeIn) { - return onAppear(sceneRoot, startValues, visInfo.startVisibility, - endValues, visInfo.endVisibility); - } else { - return onDisappear(sceneRoot, startValues, visInfo.startVisibility, - endValues, visInfo.endVisibility - ); - } + if (visInfo.visibilityChange + && (visInfo.startParent != null || visInfo.endParent != null)) { + if (visInfo.fadeIn) { + return onAppear(sceneRoot, startValues, visInfo.startVisibility, + endValues, visInfo.endVisibility); + } else { + return onDisappear(sceneRoot, startValues, visInfo.startVisibility, + endValues, visInfo.endVisibility + ); } } return null; diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 5653066..17035b1 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -19,7 +19,11 @@ package android.view; import android.graphics.Bitmap; import android.graphics.Rect; import android.graphics.SurfaceTexture; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; import android.os.Trace; +import android.util.Log; import android.util.TimeUtils; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; @@ -65,6 +69,8 @@ public class ThreadedRenderer extends HardwareRenderer { private Choreographer mChoreographer; ThreadedRenderer(boolean translucent) { + AtlasInitializer.sInstance.init(); + long rootNodePtr = nCreateRootRenderNode(); mRootNode = RenderNode.adopt(rootNodePtr); mRootNode.setClipToBounds(false); @@ -292,8 +298,43 @@ public class ThreadedRenderer extends HardwareRenderer { } } - /** @hide */ - public static native void postToRenderThread(Runnable runnable); + private static class AtlasInitializer { + static AtlasInitializer sInstance = new AtlasInitializer(); + + private boolean mInitialized = false; + + private AtlasInitializer() {} + + synchronized void init() { + if (mInitialized) return; + IBinder binder = ServiceManager.getService("assetatlas"); + if (binder == null) return; + + IAssetAtlas atlas = IAssetAtlas.Stub.asInterface(binder); + try { + if (atlas.isCompatible(android.os.Process.myPpid())) { + GraphicBuffer buffer = atlas.getBuffer(); + if (buffer != null) { + long[] map = atlas.getMap(); + if (map != null) { + nSetAtlas(buffer, map); + mInitialized = true; + } + // If IAssetAtlas is not the same class as the IBinder + // we are using a remote service and we can safely + // destroy the graphic buffer + if (atlas.getClass() != binder.getClass()) { + buffer.destroy(); + } + } + } + } catch (RemoteException e) { + Log.w(LOG_TAG, "Could not acquire atlas", e); + } + } + } + + private static native void nSetAtlas(GraphicBuffer buffer, long[] map); private static native long nCreateRootRenderNode(); private static native long nCreateProxy(boolean translucent, long rootRenderNode); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index bef96b1..e829141 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -670,7 +670,7 @@ import java.util.concurrent.atomic.AtomicInteger; * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack * @attr ref android.R.styleable#View_stateListAnimator - * @attr ref android.R.styleable#View_sharedElementName + * @attr ref android.R.styleable#View_viewName * @attr ref android.R.styleable#View_soundEffectsEnabled * @attr ref android.R.styleable#View_tag * @attr ref android.R.styleable#View_textAlignment @@ -3185,6 +3185,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private int mBackgroundResource; private boolean mBackgroundSizeChanged; + private String mViewName; + static class ListenerInfo { /** * Listener used to dispatch focus change events. @@ -3997,8 +3999,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, case R.styleable.View_accessibilityLiveRegion: setAccessibilityLiveRegion(a.getInt(attr, ACCESSIBILITY_LIVE_REGION_DEFAULT)); break; - case R.styleable.View_sharedElementName: - setSharedElementName(a.getString(attr)); + case R.styleable.View_viewName: + setViewName(a.getString(attr)); break; case R.styleable.View_nestedScrollingEnabled: setNestedScrollingEnabled(a.getBoolean(attr, false)); @@ -18839,15 +18841,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Adds all Views that have {@link #getSharedElementName()} non-null to sharedElements. - * @param sharedElements Will contain all Views in the hierarchy having a shared element name. + * Adds all Views that have {@link #getViewName()} non-null to namedElements. + * @param namedElements Will contain all Views in the hierarchy having a view name. * @hide */ - public void findSharedElements(Map<String, View> sharedElements) { + public void findNamedViews(Map<String, View> namedElements) { if (getVisibility() == VISIBLE) { - String sharedElementName = getSharedElementName(); - if (sharedElementName != null) { - sharedElements.put(sharedElementName, this); + String viewName = getViewName(); + if (viewName != null) { + namedElements.put(viewName, this); } } } @@ -19247,32 +19249,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Specifies that the shared name of the View to be shared with another Activity. - * When transitioning between Activities, the name links a UI element in the starting - * Activity to UI element in the called Activity. Names should be unique in the - * View hierarchy. + * Sets the name of the View to be used to identify Views in Transitions. + * Names should be unique in the View hierarchy. * - * @param sharedElementName The cross-Activity View identifier. The called Activity will use - * the name to match the location with a View in its layout. - * @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.os.Bundle) + * @param viewName The name of the View to uniquely identify it for Transitions. */ - public void setSharedElementName(String sharedElementName) { - setTagInternal(com.android.internal.R.id.shared_element_name, sharedElementName); + public final void setViewName(String viewName) { + mViewName = viewName; } /** - * Returns the shared name of the View to be shared with another Activity. - * When transitioning between Activities, the name links a UI element in the starting - * Activity to UI element in the called Activity. Names should be unique in the - * View hierarchy. + * Returns the name of the View to be used to identify Views in Transitions. + * Names should be unique in the View hierarchy. * - * <p>This returns null if the View is not a shared element or the name if it is.</p> + * <p>This returns null if the View has not been given a name.</p> * - * @return The name used for this View for cross-Activity transitions or null if - * this View has not been identified as shared. + * @return The name used of the View to be used to identify Views in Transitions or null + * if no name has been given. */ - public String getSharedElementName() { - return (String) getTag(com.android.internal.R.id.shared_element_name); + public String getViewName() { + return mViewName; } /** diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 4309366..92a42b7 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2301,13 +2301,13 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * individually during the transition. * @return True if the ViewGroup should be acted on together during an Activity transition. * The default value is false when the background is null and true when the background - * is not null or if {@link #getSharedElementName()} is not null. + * is not null or if {@link #getViewName()} is not null. */ public boolean isTransitionGroup() { if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) { return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0); } else { - return getBackground() != null || getSharedElementName() != null; + return getBackground() != null || getViewName() != null; } } @@ -5956,15 +5956,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** @hide */ @Override - public void findSharedElements(Map<String, View> sharedElements) { + public void findNamedViews(Map<String, View> namedElements) { if (getVisibility() != VISIBLE) { return; } - super.findSharedElements(sharedElements); + super.findNamedViews(namedElements); int count = getChildCount(); for (int i = 0; i < count; i++) { View child = getChildAt(i); - child.findSharedElements(sharedElements); + child.findNamedViews(namedElements); } } |
