diff options
Diffstat (limited to 'core/java')
41 files changed, 1204 insertions, 557 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 2cb3f39..ecd0050 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -689,6 +689,8 @@ public class Activity extends ContextThemeWrapper private static final String SAVED_DIALOGS_TAG = "android:savedDialogs"; private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_"; private static final String SAVED_DIALOG_ARGS_KEY_PREFIX = "android:dialog_args_"; + private static final String HAS_CURENT_PERMISSIONS_REQUEST_KEY = + "android:hasCurrentPermissionsRequest"; private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:"; @@ -797,6 +799,8 @@ public class Activity extends ContextThemeWrapper SharedElementCallback mEnterTransitionListener = SharedElementCallback.NULL_CALLBACK; SharedElementCallback mExitTransitionListener = SharedElementCallback.NULL_CALLBACK; + private boolean mHasCurrentPermissionsRequest; + /** Return the intent that started this activity. */ public Intent getIntent() { return mIntent; @@ -1298,6 +1302,7 @@ public class Activity extends ContextThemeWrapper onSaveInstanceState(outState); saveManagedDialogs(outState); mActivityTransitionState.saveState(outState); + storeHasCurrentPermissionRequest(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState); } @@ -1313,6 +1318,7 @@ public class Activity extends ContextThemeWrapper final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { onSaveInstanceState(outState, outPersistentState); saveManagedDialogs(outState); + storeHasCurrentPermissionRequest(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState + ", " + outPersistentState); } @@ -3811,8 +3817,15 @@ public class Activity extends ContextThemeWrapper * @see #shouldShowRequestPermissionRationale(String) */ public final void requestPermissions(@NonNull String[] permissions, int requestCode) { + if (mHasCurrentPermissionsRequest) { + Log.w(TAG, "Can reqeust only one set of permissions at a time"); + // Dispatch the callback with empty arrays which means a cancellation. + onRequestPermissionsResult(requestCode, new String[0], new int[0]); + return; + } Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions); startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null); + mHasCurrentPermissionsRequest = true; } /** @@ -6234,12 +6247,14 @@ public class Activity extends ContextThemeWrapper } final void performCreate(Bundle icicle) { + restoreHasCurrentPermissionRequest(icicle); onCreate(icicle); mActivityTransitionState.readState(icicle); performCreateCommon(); } final void performCreate(Bundle icicle, PersistableBundle persistentState) { + restoreHasCurrentPermissionRequest(icicle); onCreate(icicle, persistentState); mActivityTransitionState.readState(icicle); performCreateCommon(); @@ -6418,6 +6433,19 @@ public class Activity extends ContextThemeWrapper return mResumed; } + private void storeHasCurrentPermissionRequest(Bundle bundle) { + if (bundle != null && mHasCurrentPermissionsRequest) { + bundle.putBoolean(HAS_CURENT_PERMISSIONS_REQUEST_KEY, true); + } + } + + private void restoreHasCurrentPermissionRequest(Bundle bundle) { + if (bundle != null) { + mHasCurrentPermissionsRequest = bundle.getBoolean( + HAS_CURENT_PERMISSIONS_REQUEST_KEY, false); + } + } + void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data) { if (false) Log.v( @@ -6545,6 +6573,7 @@ public class Activity extends ContextThemeWrapper } private void dispatchRequestPermissionsResult(int requestCode, Intent data) { + mHasCurrentPermissionsRequest = false; // If the package installer crashed we may have not data - best effort. String[] permissions = (data != null) ? data.getStringArrayExtra( PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0]; diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 1b89a38..c0cd9ec 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1664,7 +1664,8 @@ final class ApplicationPackageManager extends PackageManager { // System apps and apps demanding internal storage can't be moved // anywhere else if (app.isSystemApp() - || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { + || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY + || app.installLocation == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) { return false; } diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index d50483e..29e8dd4 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -460,9 +460,6 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene // If set this fragment is being retained across the current config change. boolean mRetaining; - // If set this fragment's loaders are being retained across the current config change. - boolean mRetainLoader; - // If set this fragment has menu items to contribute. boolean mHasMenu; @@ -2404,7 +2401,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false); } if (mLoaderManager != null) { - if (mRetainLoader) { + if (mHost.getRetainLoaders()) { mLoaderManager.doRetain(); } else { mLoaderManager.doStop(); diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java index 1b45137..28dadfa 100644 --- a/core/java/android/app/FragmentController.java +++ b/core/java/android/app/FragmentController.java @@ -341,7 +341,6 @@ public class FragmentController { */ public void doLoaderStop(boolean retain) { mHost.doLoaderStop(retain); - mHost.mFragmentManager.setRetainLoader(retain); } /** diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java index 7b01307..13517e6 100644 --- a/core/java/android/app/FragmentHostCallback.java +++ b/core/java/android/app/FragmentHostCallback.java @@ -42,9 +42,14 @@ public abstract class FragmentHostCallback<E> extends FragmentContainer { private final Handler mHandler; final int mWindowAnimations; final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl(); + /** The loader managers for individual fragments [i.e. Fragment#getLoaderManager()] */ private ArrayMap<String, LoaderManager> mAllLoaderManagers; + /** Whether or not fragment loaders should retain their state */ + private boolean mRetainLoaders; + /** The loader manger for the fragment host [i.e. Activity#getLoaderManager()] */ private LoaderManagerImpl mLoaderManager; private boolean mCheckedForLoaderManager; + /** Whether or not the fragment host loader manager was started */ private boolean mLoadersStarted; public FragmentHostCallback(Context context, Handler handler, int windowAnimations) { @@ -166,6 +171,10 @@ public abstract class FragmentHostCallback<E> extends FragmentContainer { return true; } + boolean getRetainLoaders() { + return mRetainLoaders; + } + Activity getActivity() { return mActivity; } @@ -217,6 +226,8 @@ public abstract class FragmentHostCallback<E> extends FragmentContainer { } void doLoaderStop(boolean retain) { + mRetainLoaders = retain; + if (mLoaderManager == null) { return; } diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 51d6132..696ccdb 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -869,17 +869,6 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate } } - void setRetainLoader(boolean retain) { - if (mActive != null) { - for (int i=0; i<mActive.size(); i++) { - Fragment f = mActive.get(i); - if (f != null) { - f.mRetainLoader = retain; - } - } - } - } - void moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive) { if (DEBUG && false) Log.v(TAG, "moveToState: " + f @@ -2221,6 +2210,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate // This fragment was retained from a previous instance; get it // going now. fragment.mInLayout = true; + fragment.mHost = mHost; // If this fragment is newly instantiated (either right now, or // from last saved state), then give it the attributes to // initialize itself. diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 5c5562d..cd07c9c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3970,13 +3970,18 @@ public class Notification implements Parcelable return this; } + /** @hide */ + public static final int MIN_ASHMEM_BITMAP_SIZE = 128 * (1 << 10); + /** * @hide */ @Override public void purgeResources() { super.purgeResources(); - if (mPicture != null && mPicture.isMutable()) { + if (mPicture != null && + mPicture.isMutable() && + mPicture.getAllocationByteCount() >= MIN_ASHMEM_BITMAP_SIZE) { mPicture = mPicture.createAshmemBitmap(); } if (mBigLargeIcon != null) { diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 948ea1e..498ff81 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -71,10 +71,11 @@ public abstract class UsageStatsManagerInternal { * Could be hours, could be days, who knows? * * @param packageName + * @param uidForAppId The uid of the app, which will be used for its app id * @param userId * @return */ - public abstract boolean isAppIdle(String packageName, int userId); + public abstract boolean isAppIdle(String packageName, int uidForAppId, int userId); /** * Returns all of the uids for a given user where all packages associating with that uid diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 1692920..fc40a73 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -646,20 +646,18 @@ public abstract class Context { /** * Open a private file associated with this Context's application package - * for writing. Creates the file if it doesn't already exist. - * - * <p>No permissions are required to invoke this method, since it uses internal - * storage. + * for writing. Creates the file if it doesn't already exist. + * <p> + * No additional permissions are required for the calling app to read or + * write the returned file. * * @param name The name of the file to open; can not contain path - * separators. - * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the - * default operation, {@link #MODE_APPEND} to append to an existing file, - * {@link #MODE_WORLD_READABLE} and {@link #MODE_WORLD_WRITEABLE} to control - * permissions. - * + * separators. + * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the + * default operation, {@link #MODE_APPEND} to append to an + * existing file, {@link #MODE_WORLD_READABLE} and + * {@link #MODE_WORLD_WRITEABLE} to control permissions. * @return The resulting {@link FileOutputStream}. - * * @see #MODE_APPEND * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE @@ -692,6 +690,9 @@ public abstract class Context { /** * Returns the absolute path on the filesystem where a file created with * {@link #openFileOutput} is stored. + * <p> + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. * * @param name The name of the file for which you would like to get * its path. @@ -705,14 +706,16 @@ public abstract class Context { public abstract File getFileStreamPath(String name); /** - * Returns the absolute path to the directory on the filesystem where - * files created with {@link #openFileOutput} are stored. - * - * <p>No permissions are required to read or write to the returned path, since this - * path is internal storage. + * Returns the absolute path to the directory on the filesystem where files + * created with {@link #openFileOutput} are stored. + * <p> + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * <p> + * No additional permissions are required for the calling app to read or + * write files under the returned path. * * @return The path of the directory holding application files. - * * @see #openFileOutput * @see #getFileStreamPath * @see #getDir @@ -721,17 +724,19 @@ public abstract class Context { /** * Returns the absolute path to the directory on the filesystem similar to - * {@link #getFilesDir()}. The difference is that files placed under this - * directory will be excluded from automatic backup to remote storage. See + * {@link #getFilesDir()}. The difference is that files placed under this + * directory will be excluded from automatic backup to remote storage. See * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion * of the automatic backup mechanism in Android. + * <p> + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * <p> + * No additional permissions are required for the calling app to read or + * write files under the returned path. * - * <p>No permissions are required to read or write to the returned path, since this - * path is internal storage. - * - * @return The path of the directory holding application files that will not be - * automatically backed up to remote storage. - * + * @return The path of the directory holding application files that will not + * be automatically backed up to remote storage. * @see #openFileOutput * @see #getFileStreamPath * @see #getDir @@ -740,200 +745,256 @@ public abstract class Context { public abstract File getNoBackupFilesDir(); /** - * Returns the absolute path to the directory on the primary external filesystem - * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory() - * Environment.getExternalStorageDirectory()}) where the application can - * place persistent files it owns. These files are internal to the - * applications, and not typically visible to the user as media. - * - * <p>This is like {@link #getFilesDir()} in that these - * files will be deleted when the application is uninstalled, however there - * are some important differences: - * + * Returns the absolute path to the directory on the primary shared/external + * storage device where the application can place persistent files it owns. + * These files are internal to the applications, and not typically visible + * to the user as media. + * <p> + * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: * <ul> - * <li>External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. See the - * APIs on {@link android.os.Environment} for information in the storage state. - * <li>There is no security enforced with these files. For example, any application - * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * <li>Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + * <li>There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to * these files. * </ul> - * - * <p>Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions + * <p> + * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directories returned + * by {@link #getFilesDir()}, etc. + * <p> + * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions * are required to read or write to the returned path; it's always - * accessible to the calling app. This only applies to paths generated for - * package name of the calling application. To access paths belonging - * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} - * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. - * - * <p>On devices with multiple users (as described by {@link UserManager}), - * each user has their own isolated external storage. Applications only - * have access to the external storage for the user they're running as.</p> - * - * <p>Here is an example of typical code to manipulate a file in - * an application's private storage:</p> - * + * accessible to the calling app. This only applies to paths generated for + * package name of the calling application. To access paths belonging to + * other packages, + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. + * <p> + * On devices with multiple users (as described by {@link UserManager}), + * each user has their own isolated shared storage. Applications only have + * access to the shared storage for the user they're running as. + * <p> + * The returned path may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. + * <p> + * Here is an example of typical code to manipulate a file in an + * application's shared storage: + * </p> * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java * private_file} - * - * <p>If you supply a non-null <var>type</var> to this function, the returned - * file will be a path to a sub-directory of the given type. Though these files - * are not automatically scanned by the media scanner, you can explicitly - * add them to the media database with - * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], - * android.media.MediaScannerConnection.OnScanCompletedListener) - * MediaScannerConnection.scanFile}. - * Note that this is not the same as + * <p> + * If you supply a non-null <var>type</var> to this function, the returned + * file will be a path to a sub-directory of the given type. Though these + * files are not automatically scanned by the media scanner, you can + * explicitly add them to the media database with + * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], android.media.MediaScannerConnection.OnScanCompletedListener) + * MediaScannerConnection.scanFile}. Note that this is not the same as * {@link android.os.Environment#getExternalStoragePublicDirectory * Environment.getExternalStoragePublicDirectory()}, which provides - * directories of media shared by all applications. The - * directories returned here are - * owned by the application, and their contents will be removed when the - * application is uninstalled. Unlike + * directories of media shared by all applications. The directories returned + * here are owned by the application, and their contents will be removed + * when the application is uninstalled. Unlike * {@link android.os.Environment#getExternalStoragePublicDirectory - * Environment.getExternalStoragePublicDirectory()}, the directory - * returned here will be automatically created for you. - * - * <p>Here is an example of typical code to manipulate a picture in - * an application's private storage and add it to the media database:</p> - * + * Environment.getExternalStoragePublicDirectory()}, the directory returned + * here will be automatically created for you. + * <p> + * Here is an example of typical code to manipulate a picture in an + * application's shared storage and add it to the media database: + * </p> * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java * private_picture} * - * @param type The type of files directory to return. May be null for - * the root of the files directory or one of - * the following Environment constants for a subdirectory: - * {@link android.os.Environment#DIRECTORY_MUSIC}, - * {@link android.os.Environment#DIRECTORY_PODCASTS}, - * {@link android.os.Environment#DIRECTORY_RINGTONES}, - * {@link android.os.Environment#DIRECTORY_ALARMS}, - * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, - * {@link android.os.Environment#DIRECTORY_PICTURES}, or - * {@link android.os.Environment#DIRECTORY_MOVIES}. - * - * @return The path of the directory holding application files - * on external storage. Returns null if external storage is not currently - * mounted so it could not ensure the path exists; you will need to call - * this method again when it is available. - * + * @param type The type of files directory to return. May be {@code null} + * for the root of the files directory or one of the following + * constants for a subdirectory: + * {@link android.os.Environment#DIRECTORY_MUSIC}, + * {@link android.os.Environment#DIRECTORY_PODCASTS}, + * {@link android.os.Environment#DIRECTORY_RINGTONES}, + * {@link android.os.Environment#DIRECTORY_ALARMS}, + * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, + * {@link android.os.Environment#DIRECTORY_PICTURES}, or + * {@link android.os.Environment#DIRECTORY_MOVIES}. + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. * @see #getFilesDir - * @see android.os.Environment#getExternalStoragePublicDirectory + * @see #getExternalFilesDirs(String) + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ @Nullable public abstract File getExternalFilesDir(@Nullable String type); /** * Returns absolute paths to application-specific directories on all - * external storage devices where the application can place persistent files - * it owns. These files are internal to the application, and not typically - * visible to the user as media. + * shared/external storage devices where the application can place + * persistent files it owns. These files are internal to the application, + * and not typically visible to the user as media. * <p> - * This is like {@link #getFilesDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: + * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: * <ul> - * <li>External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. - * <li>There is no security enforced with these files. + * <li>Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + * <li>There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. * </ul> * <p> - * External storage devices returned here are considered a permanent part of - * the device, including both emulated external storage and physical media - * slots, such as SD cards in a battery compartment. The returned paths do - * not include transient devices, such as USB flash drives. + * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directories returned + * by {@link #getFilesDir()}, etc. + * <p> + * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. * <p> - * An application may store data on any or all of the returned devices. For + * An application may store data on any or all of the returned devices. For * example, an app may choose to store large files on the device with the * most available space, as measured by {@link StatFs}. * <p> - * No permissions are required to read or write to the returned paths; they - * are always accessible to the calling app. Write access outside of these - * paths on secondary external storage devices is not available. + * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. * <p> - * The first path returned is the same as {@link #getExternalFilesDir(String)}. - * Returned paths may be {@code null} if a storage device is unavailable. - * + * The returned path may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. + * + * @param type The type of files directory to return. May be {@code null} + * for the root of the files directory or one of the following + * constants for a subdirectory: + * {@link android.os.Environment#DIRECTORY_MUSIC}, + * {@link android.os.Environment#DIRECTORY_PODCASTS}, + * {@link android.os.Environment#DIRECTORY_RINGTONES}, + * {@link android.os.Environment#DIRECTORY_ALARMS}, + * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, + * {@link android.os.Environment#DIRECTORY_PICTURES}, or + * {@link android.os.Environment#DIRECTORY_MOVIES}. + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. The first path returned is the same as + * {@link #getExternalFilesDir(String)}. * @see #getExternalFilesDir(String) * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ public abstract File[] getExternalFilesDirs(String type); /** - * Return the primary external storage directory where this application's OBB - * files (if there are any) can be found. Note if the application does not have - * any OBB files, this directory may not exist. + * Return the primary shared/external storage directory where this + * application's OBB files (if there are any) can be found. Note if the + * application does not have any OBB files, this directory may not exist. * <p> - * This is like {@link #getFilesDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: + * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: * <ul> - * <li>External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. - * <li>There is no security enforced with these files. For example, any application - * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * <li>Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + * <li>There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to * these files. * </ul> * <p> * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions * are required to read or write to the returned path; it's always - * accessible to the calling app. This only applies to paths generated for - * package name of the calling application. To access paths belonging - * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} - * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. + * accessible to the calling app. This only applies to paths generated for + * package name of the calling application. To access paths belonging to + * other packages, + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. * <p> * On devices with multiple users (as described by {@link UserManager}), * multiple users may share the same OBB storage location. Applications * should ensure that multiple instances running under different users don't * interfere with each other. + * + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. + * @see #getObbDirs() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ public abstract File getObbDir(); /** * Returns absolute paths to application-specific directories on all - * external storage devices where the application's OBB files (if there are - * any) can be found. Note if the application does not have any OBB files, - * these directories may not exist. + * shared/external storage devices where the application's OBB files (if + * there are any) can be found. Note if the application does not have any + * OBB files, these directories may not exist. * <p> - * This is like {@link #getFilesDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: + * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: * <ul> - * <li>External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. - * <li>There is no security enforced with these files. + * <li>Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + * <li>There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. * </ul> * <p> - * External storage devices returned here are considered a permanent part of - * the device, including both emulated external storage and physical media - * slots, such as SD cards in a battery compartment. The returned paths do - * not include transient devices, such as USB flash drives. + * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. * <p> - * An application may store data on any or all of the returned devices. For + * An application may store data on any or all of the returned devices. For * example, an app may choose to store large files on the device with the * most available space, as measured by {@link StatFs}. * <p> - * No permissions are required to read or write to the returned paths; they - * are always accessible to the calling app. Write access outside of these - * paths on secondary external storage devices is not available. - * <p> - * The first path returned is the same as {@link #getObbDir()}. - * Returned paths may be {@code null} if a storage device is unavailable. - * + * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. + * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. The first path returned is the same as + * {@link #getObbDir()} * @see #getObbDir() * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ public abstract File[] getObbDirs(); /** - * Returns the absolute path to the application specific cache directory - * on the filesystem. These files will be ones that get deleted first when the - * device runs low on storage. - * There is no guarantee when these files will be deleted. - * + * Returns the absolute path to the application specific cache directory on + * the filesystem. These files will be ones that get deleted first when the + * device runs low on storage. There is no guarantee when these files will + * be deleted. + * <p> * <strong>Note: you should not <em>rely</em> on the system deleting these * files for you; you should always have a reasonable maximum, such as 1 MB, * for the amount of space you consume with cache files, and prune those * files when exceeding that space.</strong> + * <p> + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * <p> + * Apps require no extra permissions to read or write to the returned path, + * since this path lives in their private storage. * * @return The path of the directory holding application cache files. - * * @see #openFileOutput * @see #getFileStreamPath * @see #getDir @@ -949,6 +1010,9 @@ public abstract class Context { * This location is optimal for storing compiled or optimized code generated * by your application at runtime. * <p> + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * <p> * Apps require no extra permissions to read or write to the returned path, * since this path lives in their private storage. * @@ -957,120 +1021,161 @@ public abstract class Context { public abstract File getCodeCacheDir(); /** - * Returns the absolute path to the directory on the primary external filesystem - * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory() - * Environment.getExternalStorageDirectory()} where the application can - * place cache files it owns. These files are internal to the application, and - * not typically visible to the user as media. - * - * <p>This is like {@link #getCacheDir()} in that these - * files will be deleted when the application is uninstalled, however there - * are some important differences: - * + * Returns absolute path to application-specific directory on the primary + * shared/external storage device where the application can place cache + * files it owns. These files are internal to the application, and not + * typically visible to the user as media. + * <p> + * This is like {@link #getCacheDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: * <ul> - * <li>The platform does not always monitor the space available in external - * storage, and thus may not automatically delete these files. Currently - * the only time files here will be deleted by the platform is when running - * on {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and - * {@link android.os.Environment#isExternalStorageEmulated() - * Environment.isExternalStorageEmulated()} returns true. Note that you should - * be managing the maximum space you will use for these anyway, just like - * with {@link #getCacheDir()}. - * <li>External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. See the - * APIs on {@link android.os.Environment} for information in the storage state. - * <li>There is no security enforced with these files. For example, any application - * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * <li>The platform does not always monitor the space available in shared + * storage, and thus may not automatically delete these files. Apps should + * always manage the maximum space used in this location. Currently the only + * time files here will be deleted by the platform is when running on + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and + * {@link Environment#isExternalStorageEmulated(File)} returns true. + * <li>Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + * <li>There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to * these files. * </ul> - * - * <p>Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions + * <p> + * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directory returned by + * {@link #getCacheDir()}. + * <p> + * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions * are required to read or write to the returned path; it's always - * accessible to the calling app. This only applies to paths generated for - * package name of the calling application. To access paths belonging - * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} - * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. - * - * <p>On devices with multiple users (as described by {@link UserManager}), - * each user has their own isolated external storage. Applications only - * have access to the external storage for the user they're running as.</p> - * - * @return The path of the directory holding application cache files - * on external storage. Returns null if external storage is not currently - * mounted so it could not ensure the path exists; you will need to call - * this method again when it is available. + * accessible to the calling app. This only applies to paths generated for + * package name of the calling application. To access paths belonging to + * other packages, + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. + * <p> + * On devices with multiple users (as described by {@link UserManager}), + * each user has their own isolated shared storage. Applications only have + * access to the shared storage for the user they're running as. + * <p> + * The returned path may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. * + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. * @see #getCacheDir + * @see #getExternalCacheDirs() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ @Nullable public abstract File getExternalCacheDir(); /** * Returns absolute paths to application-specific directories on all - * external storage devices where the application can place cache files it - * owns. These files are internal to the application, and not typically - * visible to the user as media. + * shared/external storage devices where the application can place cache + * files it owns. These files are internal to the application, and not + * typically visible to the user as media. * <p> - * This is like {@link #getCacheDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: + * This is like {@link #getCacheDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: * <ul> - * <li>External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. - * <li>There is no security enforced with these files. + * <li>The platform does not always monitor the space available in shared + * storage, and thus may not automatically delete these files. Apps should + * always manage the maximum space used in this location. Currently the only + * time files here will be deleted by the platform is when running on + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and + * {@link Environment#isExternalStorageEmulated(File)} returns true. + * <li>Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + * <li>There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. * </ul> * <p> - * External storage devices returned here are considered a permanent part of - * the device, including both emulated external storage and physical media - * slots, such as SD cards in a battery compartment. The returned paths do - * not include transient devices, such as USB flash drives. + * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directory returned by + * {@link #getCacheDir()}. + * <p> + * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. * <p> - * An application may store data on any or all of the returned devices. For + * An application may store data on any or all of the returned devices. For * example, an app may choose to store large files on the device with the * most available space, as measured by {@link StatFs}. * <p> - * No permissions are required to read or write to the returned paths; they - * are always accessible to the calling app. Write access outside of these - * paths on secondary external storage devices is not available. + * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. * <p> - * The first path returned is the same as {@link #getExternalCacheDir()}. - * Returned paths may be {@code null} if a storage device is unavailable. + * The returned paths may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. The first path returned is the same as + * {@link #getExternalCacheDir()}. * @see #getExternalCacheDir() * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ public abstract File[] getExternalCacheDirs(); /** * Returns absolute paths to application-specific directories on all - * external storage devices where the application can place media files. - * These files are scanned and made available to other apps through + * shared/external storage devices where the application can place media + * files. These files are scanned and made available to other apps through * {@link MediaStore}. * <p> * This is like {@link #getExternalFilesDirs} in that these files will be * deleted when the application is uninstalled, however there are some * important differences: * <ul> - * <li>External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. - * <li>There is no security enforced with these files. + * <li>Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + * <li>There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. * </ul> * <p> - * External storage devices returned here are considered a permanent part of - * the device, including both emulated external storage and physical media - * slots, such as SD cards in a battery compartment. The returned paths do - * not include transient devices, such as USB flash drives. + * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. * <p> * An application may store data on any or all of the returned devices. For * example, an app may choose to store large files on the device with the * most available space, as measured by {@link StatFs}. * <p> - * No permissions are required to read or write to the returned paths; they - * are always accessible to the calling app. Write access outside of these - * paths on secondary external storage devices is not available. + * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. * <p> - * Returned paths may be {@code null} if a storage device is unavailable. + * The returned paths may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ public abstract File[] getExternalMediaDirs(); @@ -1093,6 +1198,12 @@ public abstract class Context { * created through a File object will only be accessible by your own * application; you can only set the mode of the entire directory, not * of individual files. + * <p> + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + * <p> + * Apps require no extra permissions to read or write to the returned path, + * since this path lives in their private storage. * * @param name Name of the directory to retrieve. This is a directory * that is created as part of your application data. @@ -1176,6 +1287,9 @@ public abstract class Context { /** * Returns the absolute path on the filesystem where a database created with * {@link #openOrCreateDatabase} is stored. + * <p> + * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. * * @param name The name of the database for which you would like to get * its path. diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java index 3efd89a..0441ccc 100644 --- a/core/java/android/content/PeriodicSync.java +++ b/core/java/android/content/PeriodicSync.java @@ -21,6 +21,8 @@ import android.os.Bundle; import android.os.Parcel; import android.accounts.Account; +import java.util.Objects; + /** * Value type that contains information about a periodic sync. */ @@ -144,7 +146,9 @@ public class PeriodicSync implements Parcelable { if (!b2.containsKey(key)) { return false; } - if (!b1.get(key).equals(b2.get(key))) { + // Null check. According to ContentResolver#validateSyncExtrasBundle null-valued keys + // are allowed in the bundle. + if (!Objects.equals(b1.get(key), b2.get(key))) { return false; } } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index da7cd85..0105e09 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -34,12 +34,16 @@ import java.lang.annotation.RetentionPolicy; */ public class ActivityInfo extends ComponentInfo implements Parcelable { + + // NOTE: When adding new data members be sure to update the copy-constructor, Parcel + // constructor, and writeToParcel. + /** * A style resource identifier (in the package's resources) of this * activity's theme. From the "theme" attribute or, if not set, 0. */ public int theme; - + /** * Constant corresponding to <code>standard</code> in * the {@link android.R.attr#launchMode} attribute. @@ -716,6 +720,7 @@ public class ActivityInfo extends ComponentInfo super(orig); theme = orig.theme; launchMode = orig.launchMode; + documentLaunchMode = orig.documentLaunchMode; permission = orig.permission; taskAffinity = orig.taskAffinity; targetActivity = orig.targetActivity; @@ -791,6 +796,7 @@ public class ActivityInfo extends ComponentInfo super.writeToParcel(dest, parcelableFlags); dest.writeInt(theme); dest.writeInt(launchMode); + dest.writeInt(documentLaunchMode); dest.writeString(permission); dest.writeString(taskAffinity); dest.writeString(targetActivity); @@ -820,6 +826,7 @@ public class ActivityInfo extends ComponentInfo super(source); theme = source.readInt(); launchMode = source.readInt(); + documentLaunchMode = source.readInt(); permission = source.readString(); taskAffinity = source.readString(); targetActivity = source.readString(); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index e9ff946..7c77f54 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -64,7 +64,9 @@ interface IPackageManager { boolean isPackageAvailable(String packageName, int userId); PackageInfo getPackageInfo(String packageName, int flags, int userId); int getPackageUid(String packageName, int userId); + int getPackageUidEtc(String packageName, int flags, int userId); int[] getPackageGids(String packageName, int userId); + int[] getPackageGidsEtc(String packageName, int flags, int userId); String[] currentToCanonicalPackageNames(in String[] names); String[] canonicalToCurrentPackageNames(in String[] names); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ae827e4..dba6d56 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -926,6 +926,7 @@ public class PackageParser { } pkg.volumeUuid = volumeUuid; + pkg.applicationInfo.volumeUuid = volumeUuid; pkg.baseCodePath = apkPath; pkg.mSignatures = null; diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index ecf558c..ba0d5be 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -78,14 +78,14 @@ public class SystemSensorManager extends SensorManager { sSensorModuleInitialized = true; nativeClassInit(); } - } - // initialize the sensor list - for (int index = 0;;++index) { - Sensor sensor = new Sensor(); - if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break; - mFullSensorsList.add(sensor); - mHandleToSensor.append(sensor.getHandle(), sensor); + // initialize the sensor list + for (int index = 0;;++index) { + Sensor sensor = new Sensor(); + if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break; + mFullSensorsList.add(sensor); + mHandleToSensor.append(sensor.getHandle(), sensor); + } } } diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index c8b45c7..3601b39 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -61,6 +61,8 @@ interface IInputManager { // Registers an input devices changed listener. void registerInputDevicesChangedListener(IInputDevicesChangedListener listener); + // Queries whether the device is currently in tablet mode + int isInTabletMode(); // Registers a tablet mode change listener void registerTabletModeChangedListener(ITabletModeChangedListener listener); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index a754d6b..618864f 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -19,6 +19,7 @@ package android.hardware.input; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; +import android.annotation.IntDef; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; @@ -39,6 +40,8 @@ import android.util.SparseArray; import android.view.InputDevice; import android.view.InputEvent; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -179,6 +182,31 @@ public final class InputManager { */ public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON}) + public @interface SwitchState {} + + /** + * Switch State: Unknown. + * + * The system has yet to report a valid value for the switch. + * @hide + */ + public static final int SWITCH_STATE_UNKNOWN = -1; + + /** + * Switch State: Off. + * @hide + */ + public static final int SWITCH_STATE_OFF = 0; + + /** + * Switch State: On. + * @hide + */ + public static final int SWITCH_STATE_ON = 1; + private InputManager(IInputManager im) { mIm = im; } @@ -339,6 +367,23 @@ public final class InputManager { } /** + * Queries whether the device is in tablet mode. + * + * @return The tablet switch state which is one of {@link #SWITCH_STATE_UNKNOWN}, + * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}. + * @hide + */ + @SwitchState + public int isInTabletMode() { + try { + return mIm.isInTabletMode(); + } catch (RemoteException ex) { + Log.w(TAG, "Could not get tablet mode state", ex); + return SWITCH_STATE_UNKNOWN; + } + } + + /** * Register a tablet mode changed listener. * * @param listener The listener to register. diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 11e0b48..6bbd9c8 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -906,8 +906,12 @@ public class ConnectivityManager { * Tells the underlying networking system that the caller wants to * begin using the named feature. The interpretation of {@code feature} * is completely up to each networking implementation. - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> + * * @param networkType specifies which network the request pertains to * @param feature the name of the feature to be used * @return an integer value representing the outcome of the request. @@ -957,8 +961,12 @@ public class ConnectivityManager { * Tells the underlying networking system that the caller is finished * using the named feature. The interpretation of {@code feature} * is completely up to each networking implementation. - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> + * * @param networkType specifies which network the request pertains to * @param feature the name of the feature that is no longer needed * @return an integer value representing the outcome of the request. @@ -1344,8 +1352,12 @@ public class ConnectivityManager { * Ensure that a network route exists to deliver traffic to the specified * host via the specified network interface. An attempt to add a route that * already exists is ignored, but treated as successful. - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> + * * @param networkType the type of the network over which traffic to the specified * host is to be routed * @param hostAddress the IP address of the host to which the route is desired @@ -1365,8 +1377,12 @@ public class ConnectivityManager { * Ensure that a network route exists to deliver traffic to the specified * host via the specified network interface. An attempt to add a route that * already exists is ignored, but treated as successful. - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> + * * @param networkType the type of the network over which traffic to the specified * host is to be routed * @param hostAddress the IP address of the host to which the route is desired @@ -1566,6 +1582,13 @@ public class ConnectivityManager { return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); } + /** {@hide} */ + public static final void enforceChangePermission(Context context) { + int uid = Binder.getCallingUid(); + Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings + .getPackageNameForUid(context, uid), true /* throwException */); + } + /** {@hide */ public static final void enforceTetherChangePermission(Context context) { if (context.getResources().getStringArray( @@ -1576,8 +1599,8 @@ public class ConnectivityManager { android.Manifest.permission.CONNECTIVITY_INTERNAL, "ConnectivityService"); } else { int uid = Binder.getCallingUid(); - Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings - .getPackageNameForUid(context, uid), true); + Settings.checkAndNoteWriteSettingsOperation(context, uid, Settings + .getPackageNameForUid(context, uid), true /* throwException */); } } @@ -1682,8 +1705,11 @@ public class ConnectivityManager { * allowed between the tethered devices and this device, though upstream net * access will of course fail until an upstream network interface becomes * active. - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> * * @param iface the interface name to tether. * @return error a {@code TETHER_ERROR} value indicating success or failure type @@ -1700,8 +1726,11 @@ public class ConnectivityManager { /** * Stop tethering the named interface. - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> * * @param iface the interface name to untether. * @return error a {@code TETHER_ERROR} value indicating success or failure type @@ -1801,8 +1830,11 @@ public class ConnectivityManager { * attempt to switch to Rndis and subsequently tether the resulting * interface on {@code true} or turn off tethering and switch off * Rndis on {@code false}. - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> * * @param enable a boolean - {@code true} to enable tethering * @return error a {@code TETHER_ERROR} value indicating success or failure type @@ -2486,8 +2518,11 @@ public class ConnectivityManager { * network may never attain, and whether a network will attain these states * is unknown prior to bringing up the network so the framework does not * know how to go about satisfing a request with these capabilities. - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> * * @param request {@link NetworkRequest} describing this request. * @param networkCallback The {@link NetworkCallback} to be utilized for this @@ -2509,8 +2544,12 @@ public class ConnectivityManager { * network is not found within the given time (in milliseconds) the * {@link NetworkCallback#unavailable} callback is called. The request must * still be released normally by calling {@link releaseNetworkRequest}. - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> + * * @param request {@link NetworkRequest} describing this request. * @param networkCallback The callbacks to be utilized for this request. Note * the callbacks must not be shared - they uniquely specify @@ -2583,8 +2622,12 @@ public class ConnectivityManager { * network may never attain, and whether a network will attain these states * is unknown prior to bringing up the network so the framework does not * know how to go about satisfing a request with these capabilities. - * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> + * * @param request {@link NetworkRequest} describing this request. * @param operation Action to perform when the network is available (corresponds * to the {@link NetworkCallback#onAvailable} call. Typically diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java index 87c063f..97bd5d2 100644 --- a/core/java/android/net/DhcpResults.java +++ b/core/java/android/net/DhcpResults.java @@ -21,7 +21,6 @@ import android.os.Parcel; import android.text.TextUtils; import android.util.Log; -import java.net.InetAddress; import java.net.Inet4Address; import java.util.Objects; @@ -34,7 +33,7 @@ import java.util.Objects; public class DhcpResults extends StaticIpConfiguration { private static final String TAG = "DhcpResults"; - public InetAddress serverAddress; + public Inet4Address serverAddress; /** Vendor specific information (from RFC 2132). */ public String vendorInfo; @@ -142,7 +141,7 @@ public class DhcpResults extends StaticIpConfiguration { private static void readFromParcel(DhcpResults dhcpResults, Parcel in) { StaticIpConfiguration.readFromParcel(dhcpResults, in); dhcpResults.leaseDuration = in.readInt(); - dhcpResults.serverAddress = NetworkUtils.unparcelInetAddress(in); + dhcpResults.serverAddress = (Inet4Address) NetworkUtils.unparcelInetAddress(in); dhcpResults.vendorInfo = in.readString(); } @@ -183,8 +182,8 @@ public class DhcpResults extends StaticIpConfiguration { public boolean setServerAddress(String addrString) { try { - serverAddress = NetworkUtils.numericToInetAddress(addrString); - } catch (IllegalArgumentException e) { + serverAddress = (Inet4Address) NetworkUtils.numericToInetAddress(addrString); + } catch (IllegalArgumentException|ClassCastException e) { Log.e(TAG, "setServerAddress failed with addrString " + addrString); return true; } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 3f40484..a83e722 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -50,12 +50,13 @@ public class NetworkPolicyManager { public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2; /* RULE_* are not masks and they must be exclusive */ + public static final int RULE_UNKNOWN = -1; /** All network traffic should be allowed. */ - public static final int RULE_ALLOW_ALL = 0x0; + public static final int RULE_ALLOW_ALL = 0; /** Reject traffic on metered networks. */ - public static final int RULE_REJECT_METERED = 0x1; + public static final int RULE_REJECT_METERED = 1; /** Reject traffic on all networks. */ - public static final int RULE_REJECT_ALL = 0x2; + public static final int RULE_REJECT_ALL = 2; public static final int FIREWALL_RULE_DEFAULT = 0; public static final int FIREWALL_RULE_ALLOW = 1; @@ -326,25 +327,4 @@ public class NetworkPolicyManager { // nothing found above; we can apply policy to UID return true; } - - /** {@hide} */ - public static void dumpPolicy(PrintWriter fout, int policy) { - fout.write("["); - if ((policy & POLICY_REJECT_METERED_BACKGROUND) != 0) { - fout.write("REJECT_METERED_BACKGROUND"); - } - fout.write("]"); - } - - /** {@hide} */ - public static void dumpRules(PrintWriter fout, int rules) { - fout.write("["); - if ((rules & RULE_REJECT_METERED) != 0) { - fout.write("REJECT_METERED"); - } else if ((rules & RULE_REJECT_ALL) != 0) { - fout.write("REJECT_ALL"); - } - fout.write("]"); - } - } diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index 57eef83..b7a411e 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -288,7 +288,8 @@ public class NetworkTemplate implements Parcelable { } else { final boolean matchesType = (sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType)); - return matchesType && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId); + return matchesType && !ArrayUtils.isEmpty(mMatchSubscriberIds) + && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId); } } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 2374899..862f4c4 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -577,7 +577,7 @@ public class Build { public static final int KITKAT = 19; /** - * Android 4.4W: KitKat for watches, snacks on the run. + * June 2014: Android 4.4W. KitKat for watches, snacks on the run. * * <p>Applications targeting this or a later release will get these * new changes in behavior:</p> @@ -595,7 +595,7 @@ public class Build { public static final int L = 21; /** - * Lollipop. A flat one with beautiful shadows. But still tasty. + * November 2014: Lollipop. A flat one with beautiful shadows. But still tasty. * * <p>Applications targeting this or a later release will get these * new changes in behavior:</p> @@ -626,12 +626,38 @@ public class Build { public static final int LOLLIPOP = 21; /** - * Lollipop with an extra sugar coating on the outside! + * March 2015: Lollipop with an extra sugar coating on the outside! */ public static final int LOLLIPOP_MR1 = 22; /** - * M comes after L. + * M is for Marshmallow! + * + * <p>Applications targeting this or a later release will get these + * new changes in behavior:</p> + * <ul> + * <li> Runtime permissions. Dangerous permissions are no longer granted at + * install time, but must be requested by the application at runtime through + * {@link android.app.Activity#requestPermissions}.</li> + * <li> Bluetooth and Wi-Fi scanning now requires holding the location permission.</li> + * <li> {@link android.app.AlarmManager#setTimeZone AlarmManager.setTimeZone} will fail if + * the given timezone is non-Olson.</li> + * <li> Activity transitions will only return shared + * elements mapped in the returned view hierarchy back to the calling activity.</li> + * <li> {@link android.view.View} allows a number of behaviors that may break + * existing apps: Canvas throws an exception if restore() is called too many times, + * widgets may return a hint size when returning UNSPECIFIED measure specs, and it + * will respect the attributes {@link android.R.attr#foreground}, + * {@link android.R.attr#foregroundGravity}, {@link android.R.attr#foregroundTint}, and + * {@link android.R.attr#foregroundTintMode}.</li> + * <li> {@link android.view.MotionEvent#getButtonState MotionEvent.getButtonState} + * will no longer report {@link android.view.MotionEvent#BUTTON_PRIMARY} + * and {@link android.view.MotionEvent#BUTTON_SECONDARY} as synonyms for + * {@link android.view.MotionEvent#BUTTON_STYLUS_PRIMARY} and + * {@link android.view.MotionEvent#BUTTON_STYLUS_SECONDARY}.</li> + * <li> {@link android.widget.ScrollView} now respects the layout param margins + * when measuring.</li> + * </ul> */ public static final int M = 23; } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 97b85e2..fdd34f5 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -471,7 +471,7 @@ public final class Debug * </tbody> * </table> */ - public String getMemoryStat(String statName) { + public String getMemoryStat(String statName) { switch(statName) { case "summary.java-heap": return Integer.toString(getSummaryJavaHeap()); @@ -1538,7 +1538,13 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo /** * Retrieves information about this processes memory usages. This information is broken down by - * how much is in use by dalivk, the native heap, and everything else. + * how much is in use by dalvik, the native heap, and everything else. + * + * <p><b>Note:</b> this method directly retrieves memory information for the give process + * from low-level data available to it. It may not be able to retrieve information about + * some protected allocations, such as graphics. If you want to be sure you can see + * all information about allocations by the process, use instead + * {@link android.app.ActivityManager#getProcessMemoryInfo(int[])}.</p> */ public static native void getMemoryInfo(MemoryInfo memoryInfo); diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 75bef5c..2567a41 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -287,8 +287,8 @@ public class Environment { } /** - * Return the primary external storage directory. This directory may not - * currently be accessible if it has been mounted by the user on their + * Return the primary shared/external storage directory. This directory may + * not currently be accessible if it has been mounted by the user on their * computer, has been removed from the device, or some other problem has * happened. You can determine its current state with * {@link #getExternalStorageState()}. @@ -302,12 +302,15 @@ public class Environment { * filesystem on a computer.</em> * <p> * On devices with multiple users (as described by {@link UserManager}), - * each user has their own isolated external storage. Applications only have - * access to the external storage for the user they're running as. + * each user has their own isolated shared storage. Applications only have + * access to the shared storage for the user they're running as. * <p> - * In devices with multiple "external" storage directories, this directory - * represents the "primary" external storage that the user will interact + * In devices with multiple shared/external storage directories, this + * directory represents the primary storage that the user will interact * with. Access to secondary storage is available through + * {@link Context#getExternalFilesDirs(String)}, + * {@link Context#getExternalCacheDirs()}, and + * {@link Context#getExternalMediaDirs()}. * <p> * Applications should not directly use this top-level directory, in order * to avoid polluting the user's root namespace. Any files that are private @@ -326,8 +329,9 @@ public class Environment { * <p> * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, if your * application only needs to store internal data, consider using - * {@link Context#getExternalFilesDir(String)} or - * {@link Context#getExternalCacheDir()}, which require no permissions to + * {@link Context#getExternalFilesDir(String)}, + * {@link Context#getExternalCacheDir()}, or + * {@link Context#getExternalMediaDirs()}, which require no permissions to * read or write. * <p> * This path may change between platform versions, so applications should @@ -336,8 +340,7 @@ public class Environment { * Here is an example of typical code to monitor the state of external * storage: * <p> - * {@sample - * development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java + * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java * monitor_storage} * * @see #getExternalStorageState() @@ -457,32 +460,32 @@ public class Environment { public static String DIRECTORY_DOCUMENTS = "Documents"; /** - * Get a top-level public external storage directory for placing files of - * a particular type. This is where the user will typically place and - * manage their own files, so you should be careful about what you put here - * to ensure you don't erase their files or get in the way of their own + * Get a top-level shared/external storage directory for placing files of a + * particular type. This is where the user will typically place and manage + * their own files, so you should be careful about what you put here to + * ensure you don't erase their files or get in the way of their own * organization. - * - * <p>On devices with multiple users (as described by {@link UserManager}), - * each user has their own isolated external storage. Applications only - * have access to the external storage for the user they're running as.</p> - * - * <p>Here is an example of typical code to manipulate a picture on - * the public external storage:</p> - * + * <p> + * On devices with multiple users (as described by {@link UserManager}), + * each user has their own isolated shared storage. Applications only have + * access to the shared storage for the user they're running as. + * </p> + * <p> + * Here is an example of typical code to manipulate a picture on the public + * shared storage: + * </p> * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java * public_picture} * - * @param type The type of storage directory to return. Should be one of - * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS}, - * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS}, - * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES}, - * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or - * {@link #DIRECTORY_DCIM}. May not be null. - * - * @return Returns the File path for the directory. Note that this - * directory may not yet exist, so you must make sure it exists before - * using it such as with {@link File#mkdirs File.mkdirs()}. + * @param type The type of storage directory to return. Should be one of + * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS}, + * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS}, + * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES}, + * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or + * {@link #DIRECTORY_DCIM}. May not be null. + * @return Returns the File path for the directory. Note that this directory + * may not yet exist, so you must make sure it exists before using + * it such as with {@link File#mkdirs File.mkdirs()}. */ public static File getExternalStoragePublicDirectory(String type) { throwIfUserRequired(); @@ -634,7 +637,7 @@ public class Environment { public static final String MEDIA_EJECTING = "ejecting"; /** - * Returns the current state of the primary "external" storage device. + * Returns the current state of the primary shared/external storage media. * * @see #getExternalStorageDirectory() * @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED}, @@ -657,8 +660,8 @@ public class Environment { } /** - * Returns the current state of the storage device that provides the given - * path. + * Returns the current state of the shared/external storage media at the + * given path. * * @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED}, * {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING}, @@ -676,7 +679,8 @@ public class Environment { } /** - * Returns whether the primary "external" storage device is removable. + * Returns whether the primary shared/external storage media is physically + * removable. * * @return true if the storage device can be removed (such as an SD card), * or false if the storage device is built in and cannot be @@ -689,8 +693,8 @@ public class Environment { } /** - * Returns whether the storage device that provides the given path is - * removable. + * Returns whether the shared/external storage media at the given path is + * physically removable. * * @return true if the storage device can be removed (such as an SD card), * or false if the storage device is built in and cannot be @@ -708,9 +712,15 @@ public class Environment { } /** - * Returns whether the primary "external" storage device is emulated. If - * true, data stored on this device will be stored on a portion of the - * internal storage system. + * Returns whether the primary shared/external storage media is emulated. + * <p> + * The contents of emulated storage devices are backed by a private user + * data partition, which means there is little benefit to apps storing data + * here instead of the private directories returned by + * {@link Context#getFilesDir()}, etc. + * <p> + * This returns true when emulated storage is backed by either internal + * storage or an adopted storage device. * * @see DevicePolicyManager#setStorageEncryption(android.content.ComponentName, * boolean) @@ -722,9 +732,16 @@ public class Environment { } /** - * Returns whether the storage device that provides the given path is - * emulated. If true, data stored on this device will be stored on a portion - * of the internal storage system. + * Returns whether the shared/external storage media at the given path is + * emulated. + * <p> + * The contents of emulated storage devices are backed by a private user + * data partition, which means there is little benefit to apps storing data + * here instead of the private directories returned by + * {@link Context#getFilesDir()}, etc. + * <p> + * This returns true when emulated storage is backed by either internal + * storage or an adopted storage device. * * @throws IllegalArgumentException if the path is not a valid storage * device. diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 1273772..5852f5f 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.IntegerRes; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -42,6 +43,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import dalvik.system.VMRuntime; + /** * Container for a message (data and object references) that can * be sent through an IBinder. A Parcel can contain both flattened data @@ -193,6 +196,7 @@ public final class Parcel { * indicating that we're responsible for its lifecycle. */ private boolean mOwnsNativeParcelObject; + private long mNativeSize; private RuntimeException mStack; @@ -244,7 +248,7 @@ public final class Parcel { private static native int nativeDataAvail(long nativePtr); private static native int nativeDataPosition(long nativePtr); private static native int nativeDataCapacity(long nativePtr); - private static native void nativeSetDataSize(long nativePtr, int size); + private static native long nativeSetDataSize(long nativePtr, int size); private static native void nativeSetDataPosition(long nativePtr, int pos); private static native void nativeSetDataCapacity(long nativePtr, int size); @@ -259,7 +263,7 @@ public final class Parcel { private static native void nativeWriteDouble(long nativePtr, double val); private static native void nativeWriteString(long nativePtr, String val); private static native void nativeWriteStrongBinder(long nativePtr, IBinder val); - private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val); + private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val); private static native byte[] nativeCreateByteArray(long nativePtr); private static native byte[] nativeReadBlob(long nativePtr); @@ -272,13 +276,13 @@ public final class Parcel { private static native FileDescriptor nativeReadFileDescriptor(long nativePtr); private static native long nativeCreate(); - private static native void nativeFreeBuffer(long nativePtr); + private static native long nativeFreeBuffer(long nativePtr); private static native void nativeDestroy(long nativePtr); private static native byte[] nativeMarshall(long nativePtr); - private static native void nativeUnmarshall( + private static native long nativeUnmarshall( long nativePtr, byte[] data, int offset, int length); - private static native void nativeAppendFrom( + private static native long nativeAppendFrom( long thisNativePtr, long otherNativePtr, int offset, int length); private static native boolean nativeHasFileDescriptors(long nativePtr); private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName); @@ -390,7 +394,7 @@ public final class Parcel { * @param size The new number of bytes in the Parcel. */ public final void setDataSize(int size) { - nativeSetDataSize(mNativePtr, size); + updateNativeSize(nativeSetDataSize(mNativePtr, size)); } /** @@ -442,11 +446,11 @@ public final class Parcel { * Set the bytes in data to be the raw bytes of this Parcel. */ public final void unmarshall(byte[] data, int offset, int length) { - nativeUnmarshall(mNativePtr, data, offset, length); + updateNativeSize(nativeUnmarshall(mNativePtr, data, offset, length)); } public final void appendFrom(Parcel parcel, int offset, int length) { - nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length); + updateNativeSize(nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length)); } /** @@ -599,7 +603,24 @@ public final class Parcel { * if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.</p> */ public final void writeFileDescriptor(FileDescriptor val) { - nativeWriteFileDescriptor(mNativePtr, val); + updateNativeSize(nativeWriteFileDescriptor(mNativePtr, val)); + } + + private void updateNativeSize(long newNativeSize) { + if (mOwnsNativeParcelObject) { + if (newNativeSize > Integer.MAX_VALUE) { + newNativeSize = Integer.MAX_VALUE; + } + if (newNativeSize != mNativeSize) { + int delta = (int) (newNativeSize - mNativeSize); + if (delta > 0) { + VMRuntime.getRuntime().registerNativeAllocation(delta); + } else { + VMRuntime.getRuntime().registerNativeFree(-delta); + } + mNativeSize = newNativeSize; + } + } } /** @@ -2545,7 +2566,7 @@ public final class Parcel { private void freeBuffer() { if (mOwnsNativeParcelObject) { - nativeFreeBuffer(mNativePtr); + updateNativeSize(nativeFreeBuffer(mNativePtr)); } } @@ -2553,6 +2574,7 @@ public final class Parcel { if (mNativePtr != 0) { if (mOwnsNativeParcelObject) { nativeDestroy(mNativePtr); + updateNativeSize(0); } mNativePtr = 0; } diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index 979c828..2445bc2 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -383,6 +383,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba final IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION); filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED); + filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION); mContext.registerReceiver(this, filter); } else { mContext.unregisterReceiver(this); @@ -395,13 +396,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) { int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1); - final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType) - : (streamType == mStreamType); - if (mSeekBar != null && streamMatch && streamValue != -1) { - final boolean muted = mAudioManager.isStreamMute(mStreamType) - || streamValue == 0; - mUiHandler.postUpdateSlider(streamValue, mLastAudibleStreamVolume, muted); - } + updateVolumeSlider(streamType, streamValue); } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) { if (mNotificationOrRing) { mRingerMode = mAudioManager.getRingerModeInternal(); @@ -409,10 +404,24 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba if (mAffectedByRingerMode) { updateSlider(); } + } else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) { + int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); + int streamVolume = mAudioManager.getStreamVolume(streamType); + updateVolumeSlider(streamType, streamVolume); } else if (NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED.equals(action)) { mZenMode = mNotificationManager.getZenMode(); updateSlider(); } } + + private void updateVolumeSlider(int streamType, int streamValue) { + final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType) + : (streamType == mStreamType); + if (mSeekBar != null && streamMatch && streamValue != -1) { + final boolean muted = mAudioManager.isStreamMute(mStreamType) + || streamValue == 0; + mUiHandler.postUpdateSlider(streamValue, mLastAudibleStreamVolume, muted); + } + } } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8182855..a3de3d1 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1437,25 +1437,6 @@ public final class Settings { } /** - * An app can use this method to check if it is currently allowed to change the network - * state. In order to be allowed to do so, an app must first declare either the - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} or - * {@link android.Manifest.permission#WRITE_SETTINGS} permission in its manifest. If it - * is currently disallowed, it can prompt the user to grant it this capability through a - * management UI by sending an Intent with action - * {@link android.provider.Settings#ACTION_MANAGE_WRITE_SETTINGS}. - * - * @param context A context - * @return true if the calling app can change the state of network, false otherwise. - * @hide - */ - public static boolean canChangeNetworkState(Context context) { - int uid = Binder.getCallingUid(); - return Settings.isCallingPackageAllowedToChangeNetworkState(context, uid, Settings - .getPackageNameForUid(context, uid), false); - } - - /** * System settings, containing miscellaneous system preferences. This * table holds simple name/value pairs. There are convenience * functions for accessing individual settings entries. @@ -8730,7 +8711,7 @@ public final class Settings { * write/modify system settings, as the condition differs for pre-M, M+, and * privileged/preinstalled apps. If the provided uid does not match the * callingPackage, a negative result will be returned. The caller is expected to have - * either WRITE_SETTINGS or CHANGE_NETWORK_STATE permission declared. + * the WRITE_SETTINGS permission declared. * * Note: if the check is successful, the operation of this app will be updated to the * current time. @@ -8746,31 +8727,22 @@ public final class Settings { /** * Performs a strict and comprehensive check of whether a calling package is allowed to * change the state of network, as the condition differs for pre-M, M+, and - * privileged/preinstalled apps. If the provided uid does not match the - * callingPackage, a negative result will be returned. The caller is expected to have - * either of CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared. - * @hide - */ - public static boolean isCallingPackageAllowedToChangeNetworkState(Context context, int uid, - String callingPackage, boolean throwException) { - return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid, - callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS, - PM_CHANGE_NETWORK_STATE, false); - } - - /** - * Performs a strict and comprehensive check of whether a calling package is allowed to - * change the state of network, as the condition differs for pre-M, M+, and - * privileged/preinstalled apps. If the provided uid does not match the - * callingPackage, a negative result will be returned. The caller is expected to have - * either CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared. + * privileged/preinstalled apps. The caller is expected to have either the + * CHANGE_NETWORK_STATE or the WRITE_SETTINGS permission declared. Either of these + * permissions allow changing network state; WRITE_SETTINGS is a runtime permission and + * can be revoked, but (except in M, excluding M MRs), CHANGE_NETWORK_STATE is a normal + * permission and cannot be revoked. See http://b/23597341 * - * Note: if the check is successful, the operation of this app will be updated to the - * current time. + * Note: if the check succeeds because the application holds WRITE_SETTINGS, the operation + * of this app will be updated to the current time. * @hide */ public static boolean checkAndNoteChangeNetworkStateOperation(Context context, int uid, String callingPackage, boolean throwException) { + if (context.checkCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE) + == PackageManager.PERMISSION_GRANTED) { + return true; + } return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid, callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS, PM_CHANGE_NETWORK_STATE, true); diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index db19f7a..8763496 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -41,6 +41,7 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.Locale; import java.util.Objects; import java.util.UUID; @@ -68,6 +69,7 @@ public class ZenModeConfig implements Parcelable { public static final int[] MINUTE_BUCKETS = generateMinuteBuckets(); private static final int SECONDS_MS = 1000; private static final int MINUTES_MS = 60 * SECONDS_MS; + private static final int DAY_MINUTES = 24 * 60; private static final int ZERO_VALUE_MS = 10 * SECONDS_MS; private static final boolean DEFAULT_ALLOW_CALLS = true; @@ -652,40 +654,68 @@ public class ZenModeConfig implements Parcelable { boolean shortVersion) { final long now = System.currentTimeMillis(); final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; - return toTimeCondition(context, now + millis, minutesFromNow, now, userHandle, - shortVersion); + return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion); } - public static Condition toTimeCondition(Context context, long time, int minutes, long now, + public static Condition toTimeCondition(Context context, long time, int minutes, int userHandle, boolean shortVersion) { - final int num, summaryResId, line1ResId; + final int num; + String summary, line1, line2; + final CharSequence formattedTime = getFormattedTime(context, time, userHandle); + final Resources res = context.getResources(); if (minutes < 60) { // display as minutes num = minutes; - summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short + int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short : R.plurals.zen_mode_duration_minutes_summary; - line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short + summary = res.getQuantityString(summaryResId, num, num, formattedTime); + int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short : R.plurals.zen_mode_duration_minutes; - } else { + line1 = res.getQuantityString(line1ResId, num, num, formattedTime); + line2 = res.getString(R.string.zen_mode_until, formattedTime); + } else if (minutes < DAY_MINUTES) { // display as hours num = Math.round(minutes / 60f); - summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short + int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short : R.plurals.zen_mode_duration_hours_summary; - line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short + summary = res.getQuantityString(summaryResId, num, num, formattedTime); + int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short : R.plurals.zen_mode_duration_hours; + line1 = res.getQuantityString(line1ResId, num, num, formattedTime); + line2 = res.getString(R.string.zen_mode_until, formattedTime); + } else { + // display as day/time + summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime); } - final String skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"; - final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); - final CharSequence formattedTime = DateFormat.format(pattern, time); - final Resources res = context.getResources(); - final String summary = res.getQuantityString(summaryResId, num, num, formattedTime); - final String line1 = res.getQuantityString(line1ResId, num, num, formattedTime); - final String line2 = res.getString(R.string.zen_mode_until, formattedTime); final Uri id = toCountdownConditionId(time); return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE, Condition.FLAG_RELEVANT_NOW); } + public static Condition toNextAlarmCondition(Context context, long now, long alarm, + int userHandle) { + final CharSequence formattedTime = getFormattedTime(context, alarm, userHandle); + final Resources res = context.getResources(); + final String line1 = res.getString(R.string.zen_mode_alarm, formattedTime); + final Uri id = toCountdownConditionId(alarm); + return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE, + Condition.FLAG_RELEVANT_NOW); + } + + private static CharSequence getFormattedTime(Context context, long time, int userHandle) { + String skeleton = "EEE " + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"); + GregorianCalendar now = new GregorianCalendar(); + GregorianCalendar endTime = new GregorianCalendar(); + endTime.setTimeInMillis(time); + if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR) + && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH) + && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) { + skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"; + } + final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); + return DateFormat.format(pattern, time); + } + // ==== Built-in system conditions ==== public static final String SYSTEM_AUTHORITY = "android"; @@ -883,11 +913,6 @@ public class ZenModeConfig implements Parcelable { return UUID.randomUUID().toString().replace("-", ""); } - public static String getConditionLine1(Context context, ZenModeConfig config, - int userHandle, boolean shortVersion) { - return getConditionLine(context, config, userHandle, true /*useLine1*/, shortVersion); - } - public static String getConditionSummary(Context context, ZenModeConfig config, int userHandle, boolean shortVersion) { return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion); @@ -906,8 +931,8 @@ public class ZenModeConfig implements Parcelable { if (time > 0) { final long now = System.currentTimeMillis(); final long span = time - now; - c = toTimeCondition(context, - time, Math.round(span / (float) MINUTES_MS), now, userHandle, shortVersion); + c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS), + userHandle, shortVersion); } final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; return TextUtils.isEmpty(rt) ? "" : rt; diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java index 10a994a..f2b6041 100644 --- a/core/java/android/text/Hyphenator.java +++ b/core/java/android/text/Hyphenator.java @@ -16,15 +16,17 @@ package android.text; -import com.android.internal.annotations.GuardedBy; - import android.annotation.Nullable; import android.util.Log; -import libcore.io.IoUtils; +import com.android.internal.annotations.GuardedBy; import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.Locale; @@ -45,19 +47,29 @@ public class Hyphenator { @GuardedBy("sLock") final static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>(); - final static Hyphenator sEmptyHyphenator = new Hyphenator(StaticLayout.nLoadHyphenator("")); + final static Hyphenator sEmptyHyphenator = + new Hyphenator(StaticLayout.nLoadHyphenator(null, 0), null); final private long mNativePtr; - private Hyphenator(long nativePtr) { + // We retain a reference to the buffer to keep the memory mapping valid + @SuppressWarnings("unused") + final private ByteBuffer mBuffer; + + private Hyphenator(long nativePtr, ByteBuffer b) { mNativePtr = nativePtr; + mBuffer = b; } - public static long get(@Nullable Locale locale) { + public long getNativePtr() { + return mNativePtr; + } + + public static Hyphenator get(@Nullable Locale locale) { synchronized (sLock) { Hyphenator result = sMap.get(locale); if (result != null) { - return result.mNativePtr; + return result; } // TODO: Convert this a proper locale-fallback system @@ -67,7 +79,7 @@ public class Hyphenator { result = sMap.get(languageOnlyLocale); if (result != null) { sMap.put(locale, result); - return result.mNativePtr; + return result; } // Fall back to script-only, if available @@ -80,22 +92,28 @@ public class Hyphenator { result = sMap.get(scriptOnlyLocale); if (result != null) { sMap.put(locale, result); - return result.mNativePtr; + return result; } } sMap.put(locale, sEmptyHyphenator); // To remember we found nothing. } - return sEmptyHyphenator.mNativePtr; + return sEmptyHyphenator; } private static Hyphenator loadHyphenator(String languageTag) { - String patternFilename = "hyph-"+languageTag.toLowerCase(Locale.US)+".pat.txt"; + String patternFilename = "hyph-" + languageTag.toLowerCase(Locale.US) + ".hyb"; File patternFile = new File(getSystemHyphenatorLocation(), patternFilename); try { - String patternData = IoUtils.readFileAsString(patternFile.getAbsolutePath()); - long nativePtr = StaticLayout.nLoadHyphenator(patternData); - return new Hyphenator(nativePtr); + RandomAccessFile f = new RandomAccessFile(patternFile, "r"); + try { + FileChannel fc = f.getChannel(); + MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); + long nativePtr = StaticLayout.nLoadHyphenator(buf, 0); + return new Hyphenator(nativePtr, buf); + } finally { + f.close(); + } } catch (IOException e) { Log.e(TAG, "error loading hyphenation " + patternFile, e); return null; @@ -148,7 +166,7 @@ public class Hyphenator { sMap.put(null, null); // TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data - String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "sa", "und-Ethi"}; + String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "und-Ethi"}; for (int i = 0; i < availableLanguages.length; i++) { String languageTag = availableLanguages[i]; Hyphenator h = loadHyphenator(languageTag); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 3b0def2..fdc6cb1 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -29,6 +29,7 @@ import android.util.Pools.SynchronizedPool; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Locale; @@ -341,7 +342,8 @@ public class StaticLayout extends Layout { private void setLocale(Locale locale) { if (!locale.equals(mLocale)) { - nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale)); + nSetLocale(mNativePtr, locale.toLanguageTag(), + Hyphenator.get(locale).getNativePtr()); mLocale = locale; } } @@ -625,7 +627,9 @@ public class StaticLayout extends Layout { chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); - if (chooseHt.length != 0) { + if (chooseHt.length == 0) { + chooseHt = null; // So that out() would not assume it has any contents + } else { if (chooseHtv == null || chooseHtv.length < chooseHt.length) { chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length); @@ -808,7 +812,7 @@ public class StaticLayout extends Layout { v = out(source, here, endPos, fmAscent, fmDescent, fmTop, fmBottom, - v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex], + v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, moreChars); @@ -1243,7 +1247,7 @@ public class StaticLayout extends Layout { private static native void nFreeBuilder(long nativePtr); private static native void nFinishBuilder(long nativePtr); - /* package */ static native long nLoadHyphenator(String patternData); + /* package */ static native long nLoadHyphenator(ByteBuffer buf, int offset); private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator); diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java index c82587b..6d1d893 100644 --- a/core/java/android/transition/ChangeBounds.java +++ b/core/java/android/transition/ChangeBounds.java @@ -432,23 +432,24 @@ public class ChangeBounds extends Transition { return anim; } } else { - int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X); - int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y); - int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X); - int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y); + sceneRoot.getLocationInWindow(tempLocation); + int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X) - tempLocation[0]; + int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y) - tempLocation[1]; + int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X) - tempLocation[0]; + int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y) - tempLocation[1]; // TODO: also handle size changes: check bounds and animate size changes if (startX != endX || startY != endY) { - sceneRoot.getLocationInWindow(tempLocation); - Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), - Bitmap.Config.ARGB_8888); + final int width = view.getWidth(); + final int height = view.getHeight(); + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); view.draw(canvas); final BitmapDrawable drawable = new BitmapDrawable(bitmap); + drawable.setBounds(startX, startY, startX + width, startY + height); final float transitionAlpha = view.getTransitionAlpha(); view.setTransitionAlpha(0); sceneRoot.getOverlay().add(drawable); - Path topLeftPath = getPathMotion().getPath(startX - tempLocation[0], - startY - tempLocation[1], endX - tempLocation[0], endY - tempLocation[1]); + Path topLeftPath = getPathMotion().getPath(startX, startY, endX, endY); PropertyValuesHolder origin = PropertyValuesHolder.ofObject( DRAWABLE_ORIGIN_PROPERTY, null, topLeftPath); ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, origin); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 84223c9..93345c2 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2355,7 +2355,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG3_APPLYING_INSETS * 1 PFLAG3_FITTING_SYSTEM_WINDOWS * 1 PFLAG3_NESTED_SCROLLING_ENABLED - * 1 PFLAG3_ASSIST_BLOCKED + * 1 PFLAG3_SCROLL_INDICATOR_TOP + * 1 PFLAG3_SCROLL_INDICATOR_BOTTOM + * 1 PFLAG3_SCROLL_INDICATOR_LEFT + * 1 PFLAG3_SCROLL_INDICATOR_RIGHT + * 1 PFLAG3_SCROLL_INDICATOR_START + * 1 PFLAG3_SCROLL_INDICATOR_END + * 1 PFLAG3_ASSIST_BLOCKED * |-------|-------|-------|-------| */ @@ -2549,7 +2555,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * <p>Indicates that we are allowing {@link ViewStructure} to traverse * into this view.<p> */ - static final int PFLAG3_ASSIST_BLOCKED = 0x100; + static final int PFLAG3_ASSIST_BLOCKED = 0x4000; /** * Always allow a user to over-scroll this view, provided it is a diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 5b042c6..13c1937 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -16,12 +16,6 @@ package android.widget; -import java.text.BreakIterator; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; - import android.R; import android.annotation.Nullable; import android.app.PendingIntent; @@ -112,6 +106,12 @@ import com.android.internal.util.GrowingArrayUtils; import com.android.internal.util.Preconditions; import com.android.internal.widget.EditableInputConnection; +import java.text.BreakIterator; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + /** * Helper class used by TextView to handle editable text views. @@ -1004,8 +1004,7 @@ public class Editor { } if (!handled && mTextActionMode != null) { - // TODO: Fix dragging in extracted mode. - if (touchPositionIsInSelection() && !mTextView.isInExtractedMode()) { + if (touchPositionIsInSelection()) { // Start a drag final int start = mTextView.getSelectionStart(); final int end = mTextView.getSelectionEnd(); @@ -4254,10 +4253,14 @@ public class Editor { positionAtCursorOffset(offset, false); } + /** + * @param offset Cursor offset. Must be in [-1, length]. + * @param parentScrolled If the parent has been scrolled or not. + */ @Override protected void positionAtCursorOffset(int offset, boolean parentScrolled) { super.positionAtCursorOffset(offset, parentScrolled); - mInWord = !getWordIteratorWithText().isBoundary(offset); + mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset); } @Override @@ -4490,10 +4493,14 @@ public class Editor { positionAtCursorOffset(offset, false); } + /** + * @param offset Cursor offset. Must be in [-1, length]. + * @param parentScrolled If the parent has been scrolled or not. + */ @Override protected void positionAtCursorOffset(int offset, boolean parentScrolled) { super.positionAtCursorOffset(offset, parentScrolled); - mInWord = !getWordIteratorWithText().isBoundary(offset); + mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset); } @Override @@ -4860,9 +4867,8 @@ public class Editor { mEndHandle.showAtLocation(endOffset); // No longer the first dragging motion, reset. - if (!(mTextView.isInExtractedMode())) { - startSelectionActionMode(); - } + startSelectionActionMode(); + mDragAcceleratorActive = false; mStartOffset = -1; mSwitchedLines = false; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index ddbaa9d..cc6ab65 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -234,6 +234,8 @@ public class ImageView extends View { if (w != mDrawableWidth || h != mDrawableHeight) { mDrawableWidth = w; mDrawableHeight = h; + // updates the matrix, which is dependent on the bounds + configureBounds(); } } /* we invalidate the whole view in this case because it's very diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 4dfa7db..abcd614 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -704,7 +704,7 @@ public class RelativeLayout extends ViewGroup { } final int heightMode; - if (params.width == LayoutParams.MATCH_PARENT) { + if (params.height == LayoutParams.MATCH_PARENT) { heightMode = MeasureSpec.EXACTLY; } else { heightMode = MeasureSpec.AT_MOST; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a1462c4..0c4b60b 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -16,6 +16,8 @@ package android.widget; +import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; + import android.R; import android.annotation.ColorInt; import android.annotation.DrawableRes; @@ -115,14 +117,14 @@ import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewParent; -import android.view.ViewStructure; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup.LayoutParams; +import android.view.ViewHierarchyEncoder; +import android.view.ViewParent; import android.view.ViewRootImpl; +import android.view.ViewStructure; import android.view.ViewTreeObserver; -import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -149,8 +151,6 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Locale; -import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; - /** * Displays text to the user and optionally allows them to edit it. A TextView * is a complete text editor, however the basic class is configured to not @@ -1474,7 +1474,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } } + } else if (mText instanceof Spannable) { + // Reset the selection. + stopTextActionMode(); + Selection.setSelection((Spannable) mText, getSelectionStart(), getSelectionEnd()); } + if (mEditor.hasSelectionController()) { mEditor.startSelectionActionMode(); } @@ -5243,14 +5248,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mEditor.mCreatedWithASelection = false; } - // Phone specific code (there is no ExtractEditText on tablets). - // ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can - // not be set. Do the test here instead. - if (isInExtractedMode() && hasSelection() && mEditor != null - && mEditor.mTextActionMode == null && isShown() && hasWindowFocus()) { - mEditor.startSelectionActionMode(); - } - unregisterForPreDraw(); return true; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 2e0bf06..7699673 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import android.animation.ObjectAnimator; +import android.annotation.NonNull; import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -29,6 +31,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.database.DataSetObserver; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Bundle; @@ -46,13 +49,18 @@ import android.service.chooser.ChooserTargetService; import android.service.chooser.IChooserTargetResult; import android.service.chooser.IChooserTargetService; import android.text.TextUtils; +import android.util.FloatProperty; import android.util.Log; import android.util.Slog; import android.view.LayoutInflater; import android.view.View; +import android.view.View.MeasureSpec; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ListView; @@ -80,6 +88,7 @@ public class ChooserActivity extends ResolverActivity { private Intent mReferrerFillInIntent; private ChooserListAdapter mChooserListAdapter; + private ChooserRowAdapter mChooserRowAdapter; private final List<ChooserTargetServiceConnection> mServiceConnections = new ArrayList<>(); @@ -253,7 +262,9 @@ public class ChooserActivity extends ResolverActivity { boolean alwaysUseOption) { final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; mChooserListAdapter = (ChooserListAdapter) adapter; - adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter)); + mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter); + mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView)); + adapterView.setAdapter(mChooserRowAdapter); if (listView != null) { listView.setItemsCanFocus(true); } @@ -362,6 +373,11 @@ public class ChooserActivity extends ResolverActivity { int targetsToQuery = 0; for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) { final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i); + if (adapter.getScore(dri) == 0) { + // A score of 0 means the app hasn't been used in some time; + // don't query it as it's not likely to be relevant. + continue; + } final ActivityInfo ai = dri.getResolveInfo().activityInfo; final Bundle md = ai.metaData; final String serviceName = md != null ? convertServiceName(ai.packageName, @@ -909,7 +925,63 @@ public class ChooserActivity extends ResolverActivity { @Override public int compare(ChooserTarget lhs, ChooserTarget rhs) { // Descending order - return (int) Math.signum(lhs.getScore() - rhs.getScore()); + return (int) Math.signum(rhs.getScore() - lhs.getScore()); + } + } + + static class RowScale { + private static final int DURATION = 400; + + float mScale; + ChooserRowAdapter mAdapter; + private final ObjectAnimator mAnimator; + + public static final FloatProperty<RowScale> PROPERTY = + new FloatProperty<RowScale>("scale") { + @Override + public void setValue(RowScale object, float value) { + object.mScale = value; + object.mAdapter.notifyDataSetChanged(); + } + + @Override + public Float get(RowScale object) { + return object.mScale; + } + }; + + public RowScale(@NonNull ChooserRowAdapter adapter, float from, float to) { + mAdapter = adapter; + mScale = from; + if (from == to) { + mAnimator = null; + return; + } + + mAnimator = ObjectAnimator.ofFloat(this, PROPERTY, from, to).setDuration(DURATION); + } + + public RowScale setInterpolator(Interpolator interpolator) { + if (mAnimator != null) { + mAnimator.setInterpolator(interpolator); + } + return this; + } + + public float get() { + return mScale; + } + + public void startAnimation() { + if (mAnimator != null) { + mAnimator.start(); + } + } + + public void cancelAnimation() { + if (mAnimator != null) { + mAnimator.cancel(); + } } } @@ -917,15 +989,51 @@ public class ChooserActivity extends ResolverActivity { private ChooserListAdapter mChooserListAdapter; private final LayoutInflater mLayoutInflater; private final int mColumnCount = 4; + private RowScale[] mServiceTargetScale; + private final Interpolator mInterpolator; public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) { mChooserListAdapter = wrappedAdapter; mLayoutInflater = LayoutInflater.from(ChooserActivity.this); + mInterpolator = AnimationUtils.loadInterpolator(ChooserActivity.this, + android.R.interpolator.decelerate_quint); + wrappedAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { super.onChanged(); + final int rcount = getServiceTargetRowCount(); + if (mServiceTargetScale == null + || mServiceTargetScale.length != rcount) { + RowScale[] old = mServiceTargetScale; + int oldRCount = old != null ? old.length : 0; + mServiceTargetScale = new RowScale[rcount]; + if (old != null && rcount > 0) { + System.arraycopy(old, 0, mServiceTargetScale, 0, + Math.min(old.length, rcount)); + } + + for (int i = rcount; i < oldRCount; i++) { + old[i].cancelAnimation(); + } + + for (int i = oldRCount; i < rcount; i++) { + final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f) + .setInterpolator(mInterpolator); + mServiceTargetScale[i] = rs; + } + + // Start the animations in a separate loop. + // The process of starting animations will result in + // binding views to set up initial values, and we must + // have ALL of the new RowScale objects created above before + // we get started. + for (int i = oldRCount; i < rcount; i++) { + mServiceTargetScale[i].startAnimation(); + } + } + notifyDataSetChanged(); } @@ -933,19 +1041,43 @@ public class ChooserActivity extends ResolverActivity { public void onInvalidated() { super.onInvalidated(); notifyDataSetInvalidated(); + if (mServiceTargetScale != null) { + for (RowScale rs : mServiceTargetScale) { + rs.cancelAnimation(); + } + } } }); } + private float getRowScale(int rowPosition) { + final int start = getCallerTargetRowCount(); + final int end = start + getServiceTargetRowCount(); + if (rowPosition >= start && rowPosition < end) { + return mServiceTargetScale[rowPosition - start].get(); + } + return 1.f; + } + @Override public int getCount() { return (int) ( - Math.ceil((float) mChooserListAdapter.getCallerTargetCount() / mColumnCount) - + Math.ceil((float) mChooserListAdapter.getServiceTargetCount() / mColumnCount) + getCallerTargetRowCount() + + getServiceTargetRowCount() + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount) ); } + public int getCallerTargetRowCount() { + return (int) Math.ceil( + (float) mChooserListAdapter.getCallerTargetCount() / mColumnCount); + } + + public int getServiceTargetRowCount() { + return (int) Math.ceil( + (float) mChooserListAdapter.getServiceTargetCount() / mColumnCount); + } + @Override public Object getItem(int position) { // We have nothing useful to return here. @@ -959,33 +1091,69 @@ public class ChooserActivity extends ResolverActivity { @Override public View getView(int position, View convertView, ViewGroup parent) { - final View[] holder; + final RowViewHolder holder; if (convertView == null) { holder = createViewHolder(parent); } else { - holder = (View[]) convertView.getTag(); + holder = (RowViewHolder) convertView.getTag(); } bindViewHolder(position, holder); - // We keep the actual list item view as the last item in the holder array - return holder[mColumnCount]; + return holder.row; } - View[] createViewHolder(ViewGroup parent) { - final View[] holder = new View[mColumnCount + 1]; - + RowViewHolder createViewHolder(ViewGroup parent) { final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent, false); + final RowViewHolder holder = new RowViewHolder(row, mColumnCount); + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + for (int i = 0; i < mColumnCount; i++) { - holder[i] = mChooserListAdapter.createView(row); - row.addView(holder[i]); + final View v = mChooserListAdapter.createView(row); + final int column = i; + v.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startSelected(holder.itemIndices[column], false, true); + } + }); + v.setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + showAppDetails( + mChooserListAdapter.resolveInfoForPosition( + holder.itemIndices[column], true)); + return true; + } + }); + row.addView(v); + holder.cells[i] = v; + + // Force height to be a given so we don't have visual disruption during scaling. + LayoutParams lp = v.getLayoutParams(); + v.measure(spec, spec); + if (lp == null) { + lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight()); + row.setLayoutParams(lp); + } else { + lp.height = v.getMeasuredHeight(); + } + } + + // Pre-measure so we can scale later. + holder.measure(); + LayoutParams lp = row.getLayoutParams(); + if (lp == null) { + lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.measuredRowHeight); + row.setLayoutParams(lp); + } else { + lp.height = holder.measuredRowHeight; } row.setTag(holder); - holder[mColumnCount] = row; return holder; } - void bindViewHolder(int rowPosition, View[] holder) { + void bindViewHolder(int rowPosition, RowViewHolder holder) { final int start = getFirstRowPosition(rowPosition); final int startType = mChooserListAdapter.getPositionTargetType(start); @@ -994,34 +1162,26 @@ public class ChooserActivity extends ResolverActivity { end--; } - final ViewGroup row = (ViewGroup) holder[mColumnCount]; - if (startType == ChooserListAdapter.TARGET_SERVICE) { - row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color)); + holder.row.setBackgroundColor( + getColor(R.color.chooser_service_row_background_color)); } else { - row.setBackground(null); + holder.row.setBackgroundColor(Color.TRANSPARENT); + } + + final int oldHeight = holder.row.getLayoutParams().height; + holder.row.getLayoutParams().height = Math.max(1, + (int) (holder.measuredRowHeight * getRowScale(rowPosition))); + if (holder.row.getLayoutParams().height != oldHeight) { + holder.row.requestLayout(); } for (int i = 0; i < mColumnCount; i++) { - final View v = holder[i]; + final View v = holder.cells[i]; if (start + i <= end) { v.setVisibility(View.VISIBLE); - final int itemIndex = start + i; - mChooserListAdapter.bindView(itemIndex, v); - v.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - startSelected(itemIndex, false, true); - } - }); - v.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - showAppDetails( - mChooserListAdapter.resolveInfoForPosition(itemIndex, true)); - return true; - } - }); + holder.itemIndices[i] = start + i; + mChooserListAdapter.bindView(holder.itemIndices[i], v); } else { v.setVisibility(View.GONE); } @@ -1048,6 +1208,25 @@ public class ChooserActivity extends ResolverActivity { } } + static class RowViewHolder { + final View[] cells; + final ViewGroup row; + int measuredRowHeight; + int[] itemIndices; + + public RowViewHolder(ViewGroup row, int cellCount) { + this.row = row; + this.cells = new View[cellCount]; + this.itemIndices = new int[cellCount]; + } + + public void measure() { + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + row.measure(spec, spec); + measuredRowHeight = row.getMeasuredHeight(); + } + } + static class ChooserTargetServiceConnection implements ServiceConnection { private final DisplayResolveInfo mOriginalTarget; private ComponentName mConnectedComponent; @@ -1199,4 +1378,44 @@ public class ChooserActivity extends ResolverActivity { mSelectedTarget = null; } } + + class OffsetDataSetObserver extends DataSetObserver { + private final AbsListView mListView; + private int mCachedViewType = -1; + private View mCachedView; + + public OffsetDataSetObserver(AbsListView listView) { + mListView = listView; + } + + @Override + public void onChanged() { + if (mResolverDrawerLayout == null) { + return; + } + + final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount(); + int offset = 0; + for (int i = 0; i < chooserTargetRows; i++) { + final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i; + final int vt = mChooserRowAdapter.getItemViewType(pos); + if (vt != mCachedViewType) { + mCachedView = null; + } + final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView); + int height = ((RowViewHolder) (v.getTag())).measuredRowHeight; + + offset += (int) (height * mChooserRowAdapter.getRowScale(pos)); + + if (vt >= 0) { + mCachedViewType = vt; + mCachedView = v; + } else { + mCachedViewType = -1; + } + } + + mResolverDrawerLayout.setCollapsibleHeightReserved(offset); + } + } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index a592c1e..00e250b 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -26,6 +26,7 @@ import android.os.AsyncTask; import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; +import android.view.View.OnAttachStateChangeListener; import android.widget.AbsListView; import com.android.internal.R; import com.android.internal.content.PackageMonitor; @@ -104,6 +105,8 @@ public class ResolverActivity extends Activity { private ResolverComparator mResolverComparator; private PickTargetOptionRequest mPickOptionRequest; + protected ResolverDrawerLayout mResolverDrawerLayout; + private boolean mRegistered; private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { @@ -254,6 +257,7 @@ public class ResolverActivity extends Activity { if (isVoiceInteraction()) { rdl.setCollapsed(false); } + mResolverDrawerLayout = rdl; } if (title == null) { @@ -344,6 +348,18 @@ public class ResolverActivity extends Activity { if (isVoiceInteraction()) { onSetupVoiceInteraction(); } + + getWindow().getDecorView().addOnAttachStateChangeListener( + new OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + v.getViewRootImpl().setDrawDuringWindowsAnimating(true); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); } /** @@ -1586,7 +1602,10 @@ public class ResolverActivity extends Activity { private void onBindView(View view, TargetInfo info) { final ViewHolder holder = (ViewHolder) view.getTag(); - holder.text.setText(info.getDisplayLabel()); + final CharSequence label = info.getDisplayLabel(); + if (!TextUtils.equals(holder.text.getText(), label)) { + holder.text.setText(info.getDisplayLabel()); + } if (showsExtendedInfo(info)) { holder.text2.setVisibility(View.VISIBLE); holder.text2.setText(info.getExtendedInfo()); diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 54eea69..fe0bc65 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -3266,16 +3266,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { int vis = show ? VISIBLE : INVISIBLE; visibilityChanged = state.targetVisibility != vis; state.targetVisibility = vis; + LayoutParams lp = (LayoutParams) view.getLayoutParams(); + if (lp.height != resolvedHeight || lp.width != resolvedWidth + || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) { + lp.height = resolvedHeight; + lp.width = resolvedWidth; + lp.gravity = resolvedGravity; + lp.rightMargin = rightMargin; + view.setLayoutParams(lp); + } if (show) { - LayoutParams lp = (LayoutParams) view.getLayoutParams(); - if (lp.height != resolvedHeight || lp.width != resolvedWidth - || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) { - lp.height = resolvedHeight; - lp.width = resolvedWidth; - lp.gravity = resolvedGravity; - lp.rightMargin = rightMargin; - view.setLayoutParams(lp); - } view.setBackgroundColor(color); } } diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java index 44df0ce..b44baa2 100644 --- a/core/java/com/android/internal/view/FloatingActionMode.java +++ b/core/java/com/android/internal/view/FloatingActionMode.java @@ -35,7 +35,7 @@ import java.util.Arrays; public class FloatingActionMode extends ActionMode { private static final int MAX_HIDE_DURATION = 3000; - private static final int MOVING_HIDE_DELAY = 300; + private static final int MOVING_HIDE_DELAY = 50; private final Context mContext; private final ActionMode.Callback2 mCallback; @@ -181,7 +181,6 @@ public class FloatingActionMode extends ActionMode { // Content rect is moving. mOriginatingView.removeCallbacks(mMovingOff); mFloatingToolbarVisibilityHelper.setMoving(true); - mFloatingToolbarVisibilityHelper.updateToolbarVisibility(); mOriginatingView.postDelayed(mMovingOff, MOVING_HIDE_DELAY); mFloatingToolbar.setContentRect(mContentRectOnScreen); @@ -189,9 +188,9 @@ public class FloatingActionMode extends ActionMode { } } else { mFloatingToolbarVisibilityHelper.setOutOfBounds(true); - mFloatingToolbarVisibilityHelper.updateToolbarVisibility(); mContentRectOnScreen.setEmpty(); } + mFloatingToolbarVisibilityHelper.updateToolbarVisibility(); mPreviousContentRectOnScreen.set(mContentRectOnScreen); } diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index ca6fe61..2a25db6 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -1488,10 +1488,9 @@ public final class FloatingToolbar { private static AnimatorSet createEnterAnimation(View view) { AnimatorSet animation = new AnimatorSet(); animation.playTogether( - ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(200), + ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150), // Make sure that view.x is always fixed throughout the duration of this animation. ObjectAnimator.ofFloat(view, View.X, view.getX(), view.getX())); - animation.setStartDelay(50); return animation; } @@ -1506,7 +1505,7 @@ public final class FloatingToolbar { View view, int startDelay, Animator.AnimatorListener listener) { AnimatorSet animation = new AnimatorSet(); animation.playTogether( - ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(200)); + ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100)); animation.setStartDelay(startDelay); animation.addListener(listener); return animation; diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 7679624..c4347f8 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -69,6 +69,12 @@ public class ResolverDrawerLayout extends ViewGroup { private int mCollapsibleHeight; private int mUncollapsibleHeight; + /** + * The height in pixels of reserved space added to the top of the collapsed UI; + * e.g. chooser targets + */ + private int mCollapsibleHeightReserved; + private int mTopOffset; private boolean mIsDragging; @@ -153,12 +159,62 @@ public class ResolverDrawerLayout extends ViewGroup { } } + public void setCollapsibleHeightReserved(int heightPixels) { + final int oldReserved = mCollapsibleHeightReserved; + mCollapsibleHeightReserved = heightPixels; + + final int dReserved = mCollapsibleHeightReserved - oldReserved; + if (dReserved != 0 && mIsDragging) { + mLastTouchY -= dReserved; + } + + final int oldCollapsibleHeight = mCollapsibleHeight; + mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight()); + + if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) { + return; + } + + invalidate(); + } + private boolean isMoving() { return mIsDragging || !mScroller.isFinished(); } + private boolean isDragging() { + return mIsDragging || getNestedScrollAxes() == SCROLL_AXIS_VERTICAL; + } + + private boolean updateCollapseOffset(int oldCollapsibleHeight, boolean remainClosed) { + if (oldCollapsibleHeight == mCollapsibleHeight) { + return false; + } + + if (isLaidOut()) { + final boolean isCollapsedOld = mCollapseOffset != 0; + if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight + && mCollapseOffset == oldCollapsibleHeight)) { + // Stay closed even at the new height. + mCollapseOffset = mCollapsibleHeight; + } else { + mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + } + final boolean isCollapsedNew = mCollapseOffset != 0; + if (isCollapsedOld != isCollapsedNew) { + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } + } else { + // Start out collapsed at first unless we restored state for otherwise + mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; + } + return true; + } + private int getMaxCollapsedHeight() { - return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight; + return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight) + + mCollapsibleHeightReserved; } public void setOnDismissedListener(OnDismissedListener listener) { @@ -676,7 +732,7 @@ public class ResolverDrawerLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); - heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; + heightUsed += getHeightUsed(child); } } @@ -688,7 +744,7 @@ public class ResolverDrawerLayout extends ViewGroup { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); - heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; + heightUsed += getHeightUsed(child); } } @@ -697,30 +753,43 @@ public class ResolverDrawerLayout extends ViewGroup { heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); mUncollapsibleHeight = heightUsed - mCollapsibleHeight; - if (isLaidOut()) { - final boolean isCollapsedOld = mCollapseOffset != 0; - if (oldCollapsibleHeight < mCollapsibleHeight - && mCollapseOffset == oldCollapsibleHeight) { - // Stay closed even at the new height. - mCollapseOffset = mCollapsibleHeight; - } else { - mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); - } - final boolean isCollapsedNew = mCollapseOffset != 0; - if (isCollapsedOld != isCollapsedNew) { - notifyViewAccessibilityStateChangedIfNeeded( - AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); - } - } else { - // Start out collapsed at first unless we restored state for otherwise - mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; - } + updateCollapseOffset(oldCollapsibleHeight, !isDragging()); mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset; setMeasuredDimension(sourceWidth, heightSize); } + private int getHeightUsed(View child) { + // This method exists because we're taking a fast path at measuring ListViews that + // lets us get away with not doing the more expensive wrap_content measurement which + // imposes double child view measurement costs. If we're looking at a ListView, we can + // check against the lowest child view plus padding and margin instead of the actual + // measured height of the ListView. This lets the ListView hang off the edge when + // all of the content would fit on-screen. + + int heightUsed = child.getMeasuredHeight(); + if (child instanceof AbsListView) { + final AbsListView lv = (AbsListView) child; + final int lvPaddingBottom = lv.getPaddingBottom(); + + int lowest = 0; + for (int i = 0, N = lv.getChildCount(); i < N; i++) { + final int bottom = lv.getChildAt(i).getBottom() + lvPaddingBottom; + if (bottom > lowest) { + lowest = bottom; + } + } + + if (lowest < heightUsed) { + heightUsed = lowest; + } + } + + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + return lp.topMargin + heightUsed + lp.bottomMargin; + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int width = getWidth(); |