diff options
67 files changed, 1637 insertions, 686 deletions
diff --git a/api/current.txt b/api/current.txt index 2d133aa8138f..7040e4d3fbe1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -40917,6 +40917,7 @@ package android.widget { method public int getInputMethodMode(); method public int getMaxAvailableHeight(android.view.View); method public int getMaxAvailableHeight(android.view.View, int); + method public int getMaxAvailableHeight(android.view.View, int, boolean); method public boolean getOverlapAnchor(); method public int getSoftInputMode(); method public int getWidth(); diff --git a/api/system-current.txt b/api/system-current.txt index 93551dad532e..9b85df443228 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -43526,6 +43526,7 @@ package android.widget { method public int getInputMethodMode(); method public int getMaxAvailableHeight(android.view.View); method public int getMaxAvailableHeight(android.view.View, int); + method public int getMaxAvailableHeight(android.view.View, int, boolean); method public boolean getOverlapAnchor(); method public int getSoftInputMode(); method public int getWidth(); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 4191dce79be8..6606f9be2e79 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -478,18 +478,26 @@ public class ActivityManager { /** * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates + * that the resize is from the window manager (instead of the user) due to a screen + * rotation change. + * @hide + */ + public static final int RESIZE_MODE_SYSTEM_SCREEN_ROTATION = 1; + + /** + * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates * that the resize is initiated by the user (most likely via a drag action on the * window's edge or corner). * @hide */ - public static final int RESIZE_MODE_USER = 1; + public static final int RESIZE_MODE_USER = 2; /** * Input parameter to {@link android.app.IActivityManager#resizeTask} which indicates * that the resize should be performed even if the bounds appears unchanged. * @hide */ - public static final int RESIZE_MODE_FORCED = 2; + public static final int RESIZE_MODE_FORCED = 3; /** @hide */ public int getFrontActivityScreenCompatMode() { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 5544a7150625..0d53dbe950ea 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -126,8 +126,14 @@ public class ApplicationPackageManager extends PackageManager { @Override public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException { + return getPackageInfoAsUser(packageName, flags, mContext.getUserId()); + } + + @Override + public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) + throws NameNotFoundException { try { - PackageInfo pi = mPM.getPackageInfo(packageName, flags, mContext.getUserId()); + PackageInfo pi = mPM.getPackageInfo(packageName, flags, userId); if (pi != null) { return pi; } @@ -1338,7 +1344,7 @@ public class ApplicationPackageManager extends PackageManager { final VerificationParams verificationParams = new VerificationParams(null, null, null, VerificationParams.NO_UID, null); installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags, - installerPackageName, verificationParams, null); + installerPackageName, verificationParams, null, UserHandle.myUserId()); } @Override @@ -1348,7 +1354,7 @@ public class ApplicationPackageManager extends PackageManager { final VerificationParams verificationParams = new VerificationParams(verificationURI, null, null, VerificationParams.NO_UID, manifestDigest); installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags, - installerPackageName, verificationParams, encryptionParams); + installerPackageName, verificationParams, encryptionParams, UserHandle.myUserId()); } @Override @@ -1356,15 +1362,23 @@ public class ApplicationPackageManager extends PackageManager { IPackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { installCommon(packageURI, new LegacyPackageInstallObserver(observer), flags, - installerPackageName, verificationParams, encryptionParams); + installerPackageName, verificationParams, encryptionParams, UserHandle.myUserId()); } @Override public void installPackage(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName) { + installPackageAsUser(packageURI, observer, flags, installerPackageName, + UserHandle.myUserId()); + } + + @Override + public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer, int flags, + String installerPackageName, int userId) { final VerificationParams verificationParams = new VerificationParams(null, null, null, VerificationParams.NO_UID, null); - installCommon(packageURI, observer, flags, installerPackageName, verificationParams, null); + installCommon(packageURI, observer, flags, installerPackageName, verificationParams, null, + userId); } @Override @@ -1375,7 +1389,7 @@ public class ApplicationPackageManager extends PackageManager { final VerificationParams verificationParams = new VerificationParams(verificationURI, null, null, VerificationParams.NO_UID, manifestDigest); installCommon(packageURI, observer, flags, installerPackageName, verificationParams, - encryptionParams); + encryptionParams, UserHandle.myUserId()); } @Override @@ -1383,12 +1397,13 @@ public class ApplicationPackageManager extends PackageManager { PackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { installCommon(packageURI, observer, flags, installerPackageName, verificationParams, - encryptionParams); + encryptionParams, UserHandle.myUserId()); } private void installCommon(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, - VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { + VerificationParams verificationParams, ContainerEncryptionParams encryptionParams, + int userId) { if (!"file".equals(packageURI.getScheme())) { throw new UnsupportedOperationException("Only file:// URIs are supported"); } @@ -1398,17 +1413,22 @@ public class ApplicationPackageManager extends PackageManager { final String originPath = packageURI.getPath(); try { - mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName, - verificationParams, null); + mPM.installPackageAsUser(originPath, observer.getBinder(), flags, installerPackageName, + verificationParams, null, userId); } catch (RemoteException ignored) { } } @Override - public int installExistingPackage(String packageName) + public int installExistingPackage(String packageName) throws NameNotFoundException { + return installExistingPackageAsUser(packageName, UserHandle.myUserId()); + } + + @Override + public int installExistingPackageAsUser(String packageName, int userId) throws NameNotFoundException { try { - int res = mPM.installExistingPackageAsUser(packageName, UserHandle.myUserId()); + int res = mPM.installExistingPackageAsUser(packageName, userId); if (res == INSTALL_FAILED_INVALID_URI) { throw new NameNotFoundException("Package " + packageName + " doesn't exist"); } @@ -1706,8 +1726,14 @@ public class ApplicationPackageManager extends PackageManager { @Override public void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) { + deletePackageAsUser(packageName, observer, flags, UserHandle.myUserId()); + } + + @Override + public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags, + int userId) { try { - mPM.deletePackageAsUser(packageName, observer, UserHandle.myUserId(), flags); + mPM.deletePackageAsUser(packageName, observer, userId, flags); } catch (RemoteException e) { // Should never happen! } diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 689283c8e550..aa4a631ea76a 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -533,6 +533,14 @@ public abstract class BackupAgent extends ContextWrapper { File file = scanQueue.remove(0); String filePath; try { + // Ignore symlinks outright + StructStat stat = Os.lstat(file.getPath()); + if (OsConstants.S_ISLNK(stat.st_mode)) { + if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file); + continue; + } + + // For all other verification, look at the canonicalized path filePath = file.getCanonicalPath(); // prune this subtree? @@ -544,11 +552,7 @@ public abstract class BackupAgent extends ContextWrapper { } // If it's a directory, enqueue its contents for scanning. - StructStat stat = Os.lstat(filePath); - if (OsConstants.S_ISLNK(stat.st_mode)) { - if (DEBUG) Log.i(TAG, "Symlink (skipping)!: " + file); - continue; - } else if (OsConstants.S_ISDIR(stat.st_mode)) { + if (OsConstants.S_ISDIR(stat.st_mode)) { File[] contents = file.listFiles(); if (contents != null) { for (File entry : contents) { diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index c8e9402e6442..697b946eec73 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2010,7 +2010,7 @@ public abstract class PackageManager { * {@link #GET_RECEIVERS}, {@link #GET_SERVICES}, * {@link #GET_SIGNATURES}, {@link #GET_UNINSTALLED_PACKAGES} to * modify the data returned. - * @return Returns a PackageInfo object containing information about the + * @return A PackageInfo object containing information about the * package. If flag GET_UNINSTALLED_PACKAGES is set and if the * package is not found in the list of installed applications, the * package information is retrieved from the list of uninstalled @@ -2032,6 +2032,46 @@ public abstract class PackageManager { throws NameNotFoundException; /** + * @hide + * Retrieve overall information about an application package that is + * installed on the system. + * <p> + * Throws {@link NameNotFoundException} if a package with the given name can + * not be found on the system. + * + * @param packageName The full name (i.e. com.google.apps.contacts) of the + * desired package. + * @param flags Additional option flags. Use any combination of + * {@link #GET_ACTIVITIES}, {@link #GET_GIDS}, + * {@link #GET_CONFIGURATIONS}, {@link #GET_INSTRUMENTATION}, + * {@link #GET_PERMISSIONS}, {@link #GET_PROVIDERS}, + * {@link #GET_RECEIVERS}, {@link #GET_SERVICES}, + * {@link #GET_SIGNATURES}, {@link #GET_UNINSTALLED_PACKAGES} to + * modify the data returned. + * @param userId The user id. + * @return A PackageInfo object containing information about the + * package. If flag GET_UNINSTALLED_PACKAGES is set and if the + * package is not found in the list of installed applications, the + * package information is retrieved from the list of uninstalled + * applications (which includes installed applications as well as + * applications with data directory i.e. applications which had been + * deleted with {@code DONT_DELETE_DATA} flag set). + * @see #GET_ACTIVITIES + * @see #GET_GIDS + * @see #GET_CONFIGURATIONS + * @see #GET_INSTRUMENTATION + * @see #GET_PERMISSIONS + * @see #GET_PROVIDERS + * @see #GET_RECEIVERS + * @see #GET_SERVICES + * @see #GET_SIGNATURES + * @see #GET_UNINSTALLED_PACKAGES + */ + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) + public abstract PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) + throws NameNotFoundException; + + /** * Map from the current package names in use on the device to whatever * the current canonical name of that package is. * @param names Array of current names to be mapped. @@ -3689,6 +3729,31 @@ public abstract class PackageManager { Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName); + + /** + * @hide + * Install a package. Since this may take a little while, the result will be + * posted back to the given observer. An installation will fail if the package named + * in the package file's manifest is already installed, or if there's no space + * available on the device. + * @param packageURI The location of the package file to install. This can be a 'file:' or a + * 'content:' URI. + * @param observer An observer callback to get notified when the package installation is + * complete. {@link PackageInstallObserver#packageInstalled(String, Bundle, int)} will be + * called when that happens. This parameter must not be null. + * @param flags - possible values: {@link #INSTALL_FORWARD_LOCK}, + * {@link #INSTALL_REPLACE_EXISTING}, {@link #INSTALL_ALLOW_TEST}. + * @param installerPackageName Optional package name of the application that is performing the + * installation. This identifies which market the package came from. + * @param userId The user id. + */ + @RequiresPermission(anyOf = { + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.INTERACT_ACROSS_USERS_FULL}) + public abstract void installPackageAsUser( + Uri packageURI, PackageInstallObserver observer, int flags, + String installerPackageName, int userId); + /** * Similar to * {@link #installPackage(Uri, IPackageInstallObserver, int, String)} but @@ -3752,7 +3817,17 @@ public abstract class PackageManager { * @hide */ // @SystemApi - public abstract int installExistingPackage(String packageName) + public abstract int installExistingPackage(String packageName) throws NameNotFoundException; + + /** + * If there is already an application with the given package name installed + * on the system for other users, also install it for the specified user. + * @hide + */ + @RequiresPermission(anyOf = { + Manifest.permission.INSTALL_PACKAGES, + Manifest.permission.INTERACT_ACROSS_USERS_FULL}) + public abstract int installExistingPackageAsUser(String packageName, int userId) throws NameNotFoundException; /** @@ -3958,7 +4033,7 @@ public abstract class PackageManager { * @param observer An observer callback to get notified when the package deletion is * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be * called when that happens. observer may be null to indicate that no callback is desired. - * @param flags - possible values: {@link #DELETE_KEEP_DATA}, + * @param flags Possible values: {@link #DELETE_KEEP_DATA}, * {@link #DELETE_ALL_USERS}. * * @hide @@ -3968,6 +4043,27 @@ public abstract class PackageManager { String packageName, IPackageDeleteObserver observer, int flags); /** + * Attempts to delete a package. Since this may take a little while, the result will + * be posted back to the given observer. A deletion will fail if the named package cannot be + * found, or if the named package is a "system package". + * (TODO: include pointer to documentation on "system packages") + * + * @param packageName The name of the package to delete + * @param observer An observer callback to get notified when the package deletion is + * complete. {@link android.content.pm.IPackageDeleteObserver#packageDeleted(boolean)} will be + * called when that happens. observer may be null to indicate that no callback is desired. + * @param flags Possible values: {@link #DELETE_KEEP_DATA}, {@link #DELETE_ALL_USERS}. + * @param userId The user Id + * + * @hide + */ + @RequiresPermission(anyOf = { + Manifest.permission.DELETE_PACKAGES, + Manifest.permission.INTERACT_ACROSS_USERS_FULL}) + public abstract void deletePackageAsUser( + String packageName, IPackageDeleteObserver observer, int flags, int userId); + + /** * Retrieve the package name of the application that installed a package. This identifies * which market the package came from. * diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 7c091575df38..4c19ddda19bd 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -49,6 +49,7 @@ interface IUserManager { UserInfo getUserInfo(int userHandle); long getUserCreationTime(int userHandle); boolean isRestricted(); + boolean canHaveRestrictedProfile(int userId); int getUserSerialNumber(int userHandle); int getUserHandle(int userSerialNumber); Bundle getUserRestrictions(int userHandle); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index f58933a82186..37467caf8bfc 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -632,6 +632,19 @@ public class UserManager { } /** + * Checks if specified user can have restricted profile. + * @hide + */ + public boolean canHaveRestrictedProfile(int userId) { + try { + return mService.canHaveRestrictedProfile(userId); + } catch (RemoteException re) { + Log.w(TAG, "Could not check if user can have restricted profile", re); + return false; + } + } + + /** * Checks if the calling app is running as a guest user. * @return whether the caller is a guest user. * @hide diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java index 37312d064948..e200bef7ec52 100644 --- a/core/java/android/view/LayoutInflater.java +++ b/core/java/android/view/LayoutInflater.java @@ -72,6 +72,9 @@ public abstract class LayoutInflater { private static final String TAG = LayoutInflater.class.getSimpleName(); private static final boolean DEBUG = false; + /** Empty stack trace used to avoid log spam in re-throw exceptions. */ + private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0]; + /** * This field should be made private, so it is hidden from the SDK. * {@hide} @@ -532,15 +535,14 @@ public abstract class LayoutInflater { } } catch (XmlPullParserException e) { - InflateException ex = new InflateException(e.getMessage()); - ex.initCause(e); - throw ex; + final InflateException ie = new InflateException(e.getMessage(), e); + ie.setStackTrace(EMPTY_STACK_TRACE); + throw ie; } catch (Exception e) { - InflateException ex = new InflateException( - parser.getPositionDescription() - + ": " + e.getMessage()); - ex.initCause(e); - throw ex; + final InflateException ie = new InflateException(parser.getPositionDescription() + + ": " + e.getMessage(), e); + ie.setStackTrace(EMPTY_STACK_TRACE); + throw ie; } finally { // Don't retain static reference on context. mConstructorArgs[0] = lastContext; @@ -625,27 +627,25 @@ public abstract class LayoutInflater { return view; } catch (NoSuchMethodException e) { - InflateException ie = new InflateException(attrs.getPositionDescription() - + ": Error inflating class " - + (prefix != null ? (prefix + name) : name)); - ie.initCause(e); + final InflateException ie = new InflateException(attrs.getPositionDescription() + + ": Error inflating class " + (prefix != null ? (prefix + name) : name), e); + ie.setStackTrace(EMPTY_STACK_TRACE); throw ie; } catch (ClassCastException e) { // If loaded class is not a View subclass - InflateException ie = new InflateException(attrs.getPositionDescription() - + ": Class is not a View " - + (prefix != null ? (prefix + name) : name)); - ie.initCause(e); + final InflateException ie = new InflateException(attrs.getPositionDescription() + + ": Class is not a View " + (prefix != null ? (prefix + name) : name), e); + ie.setStackTrace(EMPTY_STACK_TRACE); throw ie; } catch (ClassNotFoundException e) { // If loadClass fails, we should propagate the exception. throw e; } catch (Exception e) { - InflateException ie = new InflateException(attrs.getPositionDescription() - + ": Error inflating class " - + (clazz == null ? "<unknown>" : clazz.getName())); - ie.initCause(e); + final InflateException ie = new InflateException( + attrs.getPositionDescription() + ": Error inflating class " + + (clazz == null ? "<unknown>" : clazz.getName()), e); + ie.setStackTrace(EMPTY_STACK_TRACE); throw ie; } finally { Trace.traceEnd(Trace.TRACE_TAG_VIEW); @@ -657,8 +657,7 @@ public abstract class LayoutInflater { */ private void failNotAllowed(String name, String prefix, AttributeSet attrs) { throw new InflateException(attrs.getPositionDescription() - + ": Class not allowed to be inflated " - + (prefix != null ? (prefix + name) : name)); + + ": Class not allowed to be inflated "+ (prefix != null ? (prefix + name) : name)); } /** @@ -774,14 +773,14 @@ public abstract class LayoutInflater { } catch (ClassNotFoundException e) { final InflateException ie = new InflateException(attrs.getPositionDescription() - + ": Error inflating class " + name); - ie.initCause(e); + + ": Error inflating class " + name, e); + ie.setStackTrace(EMPTY_STACK_TRACE); throw ie; } catch (Exception e) { final InflateException ie = new InflateException(attrs.getPositionDescription() - + ": Error inflating class " + name); - ie.initCause(e); + + ": Error inflating class " + name, e); + ie.setStackTrace(EMPTY_STACK_TRACE); throw ie; } } diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 864a0fe72fa1..7b9de7967db3 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -18,6 +18,7 @@ package android.widget; import com.android.internal.R; +import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -1504,7 +1505,7 @@ public class PopupWindow { * @return The maximum available height for the popup to be completely * shown. */ - public int getMaxAvailableHeight(View anchor) { + public int getMaxAvailableHeight(@NonNull View anchor) { return getMaxAvailableHeight(anchor, 0); } @@ -1519,7 +1520,7 @@ public class PopupWindow { * @return The maximum available height for the popup to be completely * shown. */ - public int getMaxAvailableHeight(View anchor, int yOffset) { + public int getMaxAvailableHeight(@NonNull View anchor, int yOffset) { return getMaxAvailableHeight(anchor, yOffset, false); } @@ -1537,20 +1538,21 @@ public class PopupWindow { * bottom decorations * @return The maximum available height for the popup to be completely * shown. - * - * @hide Pending API council approval. */ - public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) { + public int getMaxAvailableHeight( + @NonNull View anchor, int yOffset, boolean ignoreBottomDecorations) { final Rect displayFrame = new Rect(); anchor.getWindowVisibleDisplayFrame(displayFrame); final int[] anchorPos = mDrawingLocation; anchor.getLocationOnScreen(anchorPos); - int bottomEdge = displayFrame.bottom; + final int bottomEdge; if (ignoreBottomDecorations) { - Resources res = anchor.getContext().getResources(); + final Resources res = anchor.getContext().getResources(); bottomEdge = res.getDisplayMetrics().heightPixels; + } else { + bottomEdge = displayFrame.bottom; } final int distanceToBottom; diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index 2a25db6b64c4..7bab446d4705 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -73,6 +73,8 @@ public final class FloatingToolbar { // This class is responsible for the public API of the floating toolbar. // It delegates rendering operations to the FloatingToolbarPopup. + public static final String FLOATING_TOOLBAR_TAG = "floating_toolbar"; + private static final MenuItem.OnMenuItemClickListener NO_OP_MENUITEM_CLICK_LISTENER = new MenuItem.OnMenuItemClickListener() { @Override @@ -1460,8 +1462,10 @@ public final class FloatingToolbar { } private static ViewGroup createContentContainer(Context context) { - return (ViewGroup) LayoutInflater.from(context) + ViewGroup contentContainer = (ViewGroup) LayoutInflater.from(context) .inflate(R.layout.floating_popup_container, null); + contentContainer.setTag(FLOATING_TOOLBAR_TAG); + return contentContainer; } private static PopupWindow createPopupWindow(View content) { diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index f7e9add58f21..60ef4a47b230 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1078,7 +1078,7 @@ public class LockPatternUtils { long deadline = getLong(LOCKOUT_ATTEMPT_DEADLINE, 0L, userId); final long timeoutMs = getLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0L, userId); final long now = SystemClock.elapsedRealtime(); - if (deadline < now) { + if (deadline < now && deadline != 0) { // timeout expired setLong(LOCKOUT_ATTEMPT_DEADLINE, 0, userId); setLong(LOCKOUT_ATTEMPT_TIMEOUT_MS, 0, userId); diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java index ce9ae02c9cec..4db1d9a56c47 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java @@ -18,8 +18,12 @@ package android.widget; import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; import static android.widget.espresso.TextViewActions.doubleTapAndDragOnText; +import static android.widget.espresso.TextViewActions.doubleClickOnTextAtIndex; import static android.widget.espresso.TextViewActions.longPressAndDragOnText; +import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex; +import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex; import static android.widget.espresso.TextViewAssertions.hasSelection; +import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed; import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.action.ViewActions.pressKey; @@ -59,6 +63,7 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV getActivity(); final String helloWorld = "Hello world!"; + onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("world"))); @@ -68,10 +73,39 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV } @SmallTest + public void testLongPressToSelect() throws Exception { + getActivity(); + + final String helloWorld = "Hello Kirk!"; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); + onView(withId(R.id.textview)).perform( + longPressOnTextAtIndex(helloWorld.indexOf("Kirk"))); + + onView(withId(R.id.textview)).check(hasSelection("Kirk")); + } + + @SmallTest + public void testLongPressEmptySpace() throws Exception { + getActivity(); + + final String helloWorld = "Hello big round sun!"; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); + // Move cursor somewhere else + onView(withId(R.id.textview)).perform(clickOnTextAtIndex(helloWorld.indexOf("big"))); + // Long-press at end of line. + onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(helloWorld.length())); + + onView(withId(R.id.textview)).check(hasInsertionPointerAtIndex(helloWorld.length())); + } + + @SmallTest public void testLongPressAndDragToSelect() throws Exception { getActivity(); final String helloWorld = "Hello little handsome boy!"; + onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); onView(withId(R.id.textview)).perform( longPressAndDragOnText(helloWorld.indexOf("little"), helloWorld.indexOf(" boy!"))); @@ -80,14 +114,60 @@ public class TextViewActivityTest extends ActivityInstrumentationTestCase2<TextV } @SmallTest + public void testDoubleTapToSelect() throws Exception { + getActivity(); + + final String helloWorld = "Hello SuetYi!"; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); + onView(withId(R.id.textview)).perform( + doubleClickOnTextAtIndex(helloWorld.indexOf("SuetYi"))); + + onView(withId(R.id.textview)).check(hasSelection("SuetYi")); + } + + @SmallTest public void testDoubleTapAndDragToSelect() throws Exception { getActivity(); final String helloWorld = "Hello young beautiful girl!"; + onView(withId(R.id.textview)).perform(click()); onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); onView(withId(R.id.textview)).perform( doubleTapAndDragOnText(helloWorld.indexOf("young"), helloWorld.indexOf(" girl!"))); onView(withId(R.id.textview)).check(hasSelection("young beautiful")); } + + @SmallTest + public void testSelectBackwordsByTouch() throws Exception { + getActivity(); + + final String helloWorld = "Hello king of the Jungle!"; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(helloWorld)); + onView(withId(R.id.textview)).perform( + doubleTapAndDragOnText(helloWorld.indexOf(" Jungle!"), helloWorld.indexOf("king"))); + + onView(withId(R.id.textview)).check(hasSelection("king of the")); + } + + @SmallTest + public void testToolbarAppearsAfterSelection() throws Exception { + getActivity(); + + // It'll be nice to check that the toolbar is not visible (or does not exist) here + // I can't currently find a way to do this. I'll get to it later. + + final String text = "Toolbar appears after selection."; + onView(withId(R.id.textview)).perform(click()); + onView(withId(R.id.textview)).perform(typeTextIntoFocusedView(text)); + onView(withId(R.id.textview)).perform( + longPressOnTextAtIndex(text.indexOf("appears"))); + + // It takes the toolbar less than 100ms to start to animate into screen. + // Ideally, we'll wait using the UiController, but I guess this works for now. + Thread.sleep(100); + assertFloatingToolbarIsDisplayed(getActivity()); + } } diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java new file mode 100644 index 000000000000..fc01d8416df3 --- /dev/null +++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.widget.espresso; + +import static android.support.test.espresso.Espresso.onView; +import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.matcher.RootMatchers.withDecorView; +import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; +import static android.support.test.espresso.matcher.ViewMatchers.withTagValue; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; + +import android.app.Activity; +import com.android.internal.widget.FloatingToolbar; + +/** + * Espresso utility methods for the floating toolbar. + */ +public class FloatingToolbarEspressoUtils { + + + private FloatingToolbarEspressoUtils() {} + + /** + * Asserts that the floating toolbar is displayed on screen. + * + * @throws AssertionError if the assertion fails + */ + public static void assertFloatingToolbarIsDisplayed(Activity activity) { + onView(withTagValue(is((Object) FloatingToolbar.FLOATING_TOOLBAR_TAG))) + .inRoot(withDecorView(not(is(activity.getWindow().getDecorView())))) + .check(matches(isDisplayed())); + } + +} diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java index 7e4735b298fe..835b1b958860 100644 --- a/core/tests/coretests/src/android/widget/espresso/TextViewActions.java +++ b/core/tests/coretests/src/android/widget/espresso/TextViewActions.java @@ -52,6 +52,36 @@ public final class TextViewActions { } /** + * Returns an action that double-clicks on text at an index on the TextView.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView displayed on screen + * <ul> + * + * @param index The index of the TextView's text to double-click on. + */ + public static ViewAction doubleClickOnTextAtIndex(int index) { + return actionWithAssertions( + new GeneralClickAction(Tap.DOUBLE, new TextCoordinates(index), Press.FINGER)); + } + + /** + * Returns an action that long presses on text at an index on the TextView.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a TextView displayed on screen + * <ul> + * + * @param index The index of the TextView's text to long press on. + */ + public static ViewAction longPressOnTextAtIndex(int index) { + return actionWithAssertions( + new GeneralClickAction(Tap.LONG, new TextCoordinates(index), Press.FINGER)); + } + + /** * Returns an action that long presses then drags on text from startIndex to endIndex on the * TextView.<br> * <br> diff --git a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java index dce3182693a6..37c7425ce478 100644 --- a/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java +++ b/core/tests/coretests/src/android/widget/espresso/TextViewAssertions.java @@ -47,7 +47,7 @@ public final class TextViewAssertions { * @param selection The expected selection. */ public static ViewAssertion hasSelection(String selection) { - return new TextSelectionAssertion(is(selection)); + return hasSelection(is(selection)); } /** @@ -66,6 +66,53 @@ public final class TextViewAssertions { } /** + * Returns a {@link ViewAssertion} that asserts that the text view insertion pointer is at + * a specified index.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a text view displayed on screen + * <ul> + * + * @param index The expected index. + */ + public static ViewAssertion hasInsertionPointerAtIndex(int index) { + return hasInsertionPointerAtIndex(is(index)); + } + + /** + * Returns a {@link ViewAssertion} that asserts that the text view insertion pointer is at + * a specified index.<br> + * <br> + * View constraints: + * <ul> + * <li>must be a text view displayed on screen + * <ul> + * + * @param index A matcher representing the expected index. + */ + public static ViewAssertion hasInsertionPointerAtIndex(final Matcher<Integer> index) { + return new ViewAssertion() { + @Override + public void check(View view, NoMatchingViewException exception) { + if (view instanceof TextView) { + TextView textView = (TextView) view; + int selectionStart = textView.getSelectionStart(); + int selectionEnd = textView.getSelectionEnd(); + try { + assertThat(selectionStart, index); + assertThat(selectionEnd, index); + } catch (IndexOutOfBoundsException e) { + throw new AssertionFailedError(e.getMessage()); + } + } else { + throw new AssertionFailedError("TextView not found"); + } + } + }; + } + + /** * A {@link ViewAssertion} to check the selected text in a {@link TextView}. */ private static final class TextSelectionAssertion implements ViewAssertion { diff --git a/graphics/tests/graphicstests/src/android/graphics/PaintTest.java b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java new file mode 100644 index 000000000000..b67aa7dca17b --- /dev/null +++ b/graphics/tests/graphicstests/src/android/graphics/PaintTest.java @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics; + +import android.test.AndroidTestCase; + +public class PaintTest extends AndroidTestCase { + public void testGetTextRunAdvances() { + { + // LTR + String text = "abcdef"; + assertGetTextRunAdvances(text, 0, text.length(), 0, text.length(), false, true); + assertGetTextRunAdvances(text, 1, text.length() - 1, 0, text.length(), false, false); + } + { + // RTL + final String text = + "\u0645\u0627\u0020\u0647\u064A\u0020\u0627\u0644\u0634" + + "\u0641\u0631\u0629\u0020\u0627\u0644\u0645\u0648\u062D" + + "\u062F\u0629\u0020\u064A\u0648\u0646\u064A\u0643\u0648" + + "\u062F\u061F"; + assertGetTextRunAdvances(text, 0, text.length(), 0, text.length(), true, true); + assertGetTextRunAdvances(text, 1, text.length() - 1, 0, text.length(), true, false); + } + } + + private void assertGetTextRunAdvances(String str, int start, int end, + int contextStart, int contextEnd, boolean isRtl, boolean compareWithOtherMethods) { + Paint p = new Paint(); + + final int count = end - start; + final float[][] advanceArrays = new float[4][count]; + + final float advance = p.getTextRunAdvances(str, start, end, contextStart, contextEnd, + isRtl, advanceArrays[0], 0); + + char chars[] = str.toCharArray(); + final float advance_c = p.getTextRunAdvances(chars, start, count, contextStart, + contextEnd - contextStart, isRtl, advanceArrays[1], 0); + assertEquals(advance, advance_c, 1.0f); + + for (int c = 1; c < count; ++c) { + final float firstPartAdvance = p.getTextRunAdvances(str, start, start + c, + contextStart, contextEnd, isRtl, advanceArrays[2], 0); + final float secondPartAdvance = p.getTextRunAdvances(str, start + c, end, + contextStart, contextEnd, isRtl, advanceArrays[2], c); + assertEquals(advance, firstPartAdvance + secondPartAdvance, 1.0f); + + + final float firstPartAdvance_c = p.getTextRunAdvances(chars, start, c, + contextStart, contextEnd - contextStart, isRtl, advanceArrays[3], 0); + final float secondPartAdvance_c = p.getTextRunAdvances(chars, start + c, + count - c, contextStart, contextEnd - contextStart, isRtl, + advanceArrays[3], c); + assertEquals(advance, firstPartAdvance_c + secondPartAdvance_c, 1.0f); + assertEquals(firstPartAdvance, firstPartAdvance_c, 1.0f); + assertEquals(secondPartAdvance, secondPartAdvance_c, 1.0f); + + for (int i = 1; i < advanceArrays.length; i++) { + for (int j = 0; j < count; j++) { + assertEquals(advanceArrays[0][j], advanceArrays[i][j], 1.0f); + } + } + + // Compare results with measureText, getRunAdvance, and getTextWidths. + if (compareWithOtherMethods && start == contextStart && end == contextEnd) { + assertEquals(advance, p.measureText(str, start, end), 1.0f); + assertEquals(advance, p.getRunAdvance( + str, start, end, contextStart, contextEnd, isRtl, end), 1.0f); + + final float[] widths = new float[count]; + p.getTextWidths(str, start, end, widths); + for (int i = 0; i < count; i++) { + assertEquals(advanceArrays[0][i], widths[i], 1.0f); + } + } + } + } + + public void testGetTextRunAdvances_invalid() { + Paint p = new Paint(); + String text = "test"; + + try { + p.getTextRunAdvances((String)null, 0, 0, 0, 0, false, null, 0); + fail("Should throw an IllegalArgumentException."); + } catch (IllegalArgumentException e) { + } + + try { + p.getTextRunAdvances((CharSequence)null, 0, 0, 0, 0, false, null, 0); + fail("Should throw an IllegalArgumentException."); + } catch (IllegalArgumentException e) { + } + + try { + p.getTextRunAdvances((char[])null, 0, 0, 0, 0, false, null, 0); + fail("Should throw an IllegalArgumentException."); + } catch (IllegalArgumentException e) { + } + + try { + p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false, + new float[text.length() - 1], 0); + fail("Should throw an IndexOutOfBoundsException."); + } catch (IndexOutOfBoundsException e) { + } + + try { + p.getTextRunAdvances(text, 0, text.length(), 0, text.length(), false, + new float[text.length()], 1); + fail("Should throw an IndexOutOfBoundsException."); + } catch (IndexOutOfBoundsException e) { + } + + // 0 > contextStart + try { + p.getTextRunAdvances(text, 0, text.length(), -1, text.length(), false, null, 0); + fail("Should throw an IndexOutOfBoundsException."); + } catch (IndexOutOfBoundsException e) { + } + + // contextStart > start + try { + p.getTextRunAdvances(text, 0, text.length(), 1, text.length(), false, null, 0); + fail("Should throw an IndexOutOfBoundsException."); + } catch (IndexOutOfBoundsException e) { + } + + // start > end + try { + p.getTextRunAdvances(text, 1, 0, 0, text.length(), false, null, 0); + fail("Should throw an IndexOutOfBoundsException."); + } catch (IndexOutOfBoundsException e) { + } + + // end > contextEnd + try { + p.getTextRunAdvances(text, 0, text.length(), 0, text.length() - 1, false, null, 0); + fail("Should throw an IndexOutOfBoundsException."); + } catch (IndexOutOfBoundsException e) { + } + + // contextEnd > text.length + try { + p.getTextRunAdvances(text, 0, text.length(), 0, text.length() + 1, false, null, 0); + fail("Should throw an IndexOutOfBoundsException."); + } catch (IndexOutOfBoundsException e) { + } + } +} diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index 2893b999997f..6ef1adac3e02 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -19,9 +19,6 @@ <!-- thickness (width) of the navigation bar on phones that require it --> <dimen name="navigation_bar_size">@*android:dimen/navigation_bar_width</dimen> - <!-- The side padding for the task stack as a percentage of the width. --> - <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.26</item> - <!-- Standard notification width + gravity --> <dimen name="notification_panel_width">@dimen/standard_notification_panel_width</dimen> <integer name="notification_panel_layout_gravity">@integer/standard_notification_panel_layout_gravity</integer> diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index 81ca86bcb9ef..f084bc2c3ddb 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -16,10 +16,6 @@ */ --> <resources> - <!-- Recent Applications parameters --> - <!-- The side padding for the task stack as a percentage of the width. --> - <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.25</item> - <fraction name="keyguard_clock_y_fraction_max">37%</fraction> <fraction name="keyguard_clock_y_fraction_min">20%</fraction> diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml index 83477c03d1e4..4f6d209b3f7a 100644 --- a/packages/SystemUI/res/values-sw600dp/config.xml +++ b/packages/SystemUI/res/values-sw600dp/config.xml @@ -32,9 +32,4 @@ <!-- Set to true to enable the user switcher on the keyguard. --> <bool name="config_keyguardUserSwitcher">true</bool> - - <!-- Transposes the search bar layout in landscape. --> - <bool name="recents_has_transposed_search_bar">true</bool> - <!-- Transposes the nav bar in landscape (only used for purposes of layout). --> - <bool name="recents_has_transposed_nav_bar">false</bool> </resources> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 6deb818ade04..49dbac2f5f65 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -39,12 +39,6 @@ <!-- On tablets this is just the close_handle_height --> <dimen name="peek_height">@dimen/close_handle_height</dimen> - <!-- The side padding for the task stack as a percentage of the width. --> - <item name="recents_stack_width_padding_percentage" format="float" type="dimen">0.075</item> - - <!-- The height of the search bar space. --> - <dimen name="recents_search_bar_space_height">72dp</dimen> - <!-- The fraction of the screen height where the clock on the Keyguard has its center. The max value is used when no notifications are displaying, and the min value is when the highest possible number of notifications are showing. --> diff --git a/packages/SystemUI/res/values-sw720dp/config.xml b/packages/SystemUI/res/values-sw720dp/config.xml index 1efae42eac2b..64e2760e7778 100644 --- a/packages/SystemUI/res/values-sw720dp/config.xml +++ b/packages/SystemUI/res/values-sw720dp/config.xml @@ -25,10 +25,5 @@ <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow card. --> <integer name="keyguard_max_notification_count">5</integer> - - <!-- Transposes the search bar layout in landscape. --> - <bool name="recents_has_transposed_search_bar">false</bool> - <!-- Transposes the nav bar in landscape (only used for purposes of layout). --> - <bool name="recents_has_transposed_nav_bar">false</bool> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index f28c9d3efc14..1d1958943e94 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -189,12 +189,6 @@ <!-- The delay to enforce between each alt-tab key press. --> <integer name="recents_alt_tab_key_delay">200</integer> - <!-- Transposes the search bar layout in landscape. --> - <bool name="recents_has_transposed_search_bar">true</bool> - - <!-- Transposes the nav bar in landscape (only used for purposes of layout). --> - <bool name="recents_has_transposed_nav_bar">true</bool> - <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. --> <integer name="recents_svelte_level">0</integer> diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java index c74339b9082b..a6ebc0bf0a9f 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/AnglesVarianceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesClassifier.java @@ -32,17 +32,26 @@ import java.util.List; * previously calculated angle. Then it calculates the variance of the differences from a stroke. * To the differences there is artificially added value 0.0 and the difference between the first * angle and PI (angles are in radians). It helps with strokes which have few points and punishes - * more strokes which are not smooth. This classifier also tries to split the stroke into two parts - * int the place in which the biggest angle is. It calculates the angle variance of the two parts - * and sums them up. The reason the classifier is doing this, is because some human swipes at the - * beginning go for a moment in one direction and then they rapidly change direction for the rest - * of the stroke (like a tick). The final result is the minimum of angle variance of the whole - * stroke and the sum of angle variances of the two parts split up. + * more strokes which are not smooth. + * + * This classifier also tries to split the stroke into two parts in the place in which the biggest + * angle is. It calculates the angle variance of the two parts and sums them up. The reason the + * classifier is doing this, is because some human swipes at the beginning go for a moment in one + * direction and then they rapidly change direction for the rest of the stroke (like a tick). The + * final result is the minimum of angle variance of the whole stroke and the sum of angle variances + * of the two parts split up. The classifier tries the tick option only if the first part is + * shorter than the second part. + * + * Additionally, the classifier classifies the angles as left angles (those angles which value is + * in [0.0, PI - ANGLE_DEVIATION) interval), straight angles + * ([PI - ANGLE_DEVIATION, PI + ANGLE_DEVIATION] interval) and right angles + * ((PI + ANGLE_DEVIATION, 2 * PI) interval) and then calculates the percentage of angles which are + * in the same direction (straight angles can be left angels or right angles) */ -public class AnglesVarianceClassifier extends StrokeClassifier { +public class AnglesClassifier extends StrokeClassifier { private HashMap<Stroke, Data> mStrokeMap = new HashMap<>(); - public AnglesVarianceClassifier(ClassifierData classifierData) { + public AnglesClassifier(ClassifierData classifierData) { mClassifierData = classifierData; } @@ -66,10 +75,14 @@ public class AnglesVarianceClassifier extends StrokeClassifier { @Override public float getFalseTouchEvaluation(int type, Stroke stroke) { - return AnglesVarianceEvaluator.evaluate(mStrokeMap.get(stroke).getAnglesVariance()); + Data data = mStrokeMap.get(stroke); + return AnglesVarianceEvaluator.evaluate(data.getAnglesVariance()) + + AnglesPercentageEvaluator.evaluate(data.getAnglesPercentage()); } private static class Data { + private final float ANGLE_DEVIATION = (float) Math.PI / 20.0f; + private List<Point> mLastThreePoints = new ArrayList<>(); private float mFirstAngleVariance; private float mPreviousAngle; @@ -80,6 +93,12 @@ public class AnglesVarianceClassifier extends StrokeClassifier { private float mSecondSum; private float mCount; private float mSecondCount; + private float mFirstLength; + private float mLength; + private float mAnglesCount; + private float mLeftAngles; + private float mRightAngles; + private float mStraightAngles; public Data() { mFirstAngleVariance = 0.0f; @@ -88,6 +107,8 @@ public class AnglesVarianceClassifier extends StrokeClassifier { mSumSquares = mSecondSumSquares = 0.0f; mSum = mSecondSum = 0.0f; mCount = mSecondCount = 1.0f; + mLength = mFirstLength = 0.0f; + mAnglesCount = mLeftAngles = mRightAngles = mStraightAngles = 0.0f; } public void addPoint(Point point) { @@ -95,6 +116,9 @@ public class AnglesVarianceClassifier extends StrokeClassifier { // Repetitions are being ignored so that proper angles are calculated. if (mLastThreePoints.isEmpty() || !mLastThreePoints.get(mLastThreePoints.size() - 1).equals(point)) { + if (!mLastThreePoints.isEmpty()) { + mLength += mLastThreePoints.get(mLastThreePoints.size() - 1).dist(point); + } mLastThreePoints.add(point); if (mLastThreePoints.size() == 4) { mLastThreePoints.remove(0); @@ -102,6 +126,15 @@ public class AnglesVarianceClassifier extends StrokeClassifier { float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0), mLastThreePoints.get(2)); + mAnglesCount++; + if (angle < Math.PI - ANGLE_DEVIATION) { + mLeftAngles++; + } else if (angle <= Math.PI + ANGLE_DEVIATION) { + mStraightAngles++; + } else { + mRightAngles++; + } + float difference = angle - mPreviousAngle; // If this is the biggest angle of the stroke so then we save the value of @@ -109,6 +142,7 @@ public class AnglesVarianceClassifier extends StrokeClassifier { // variance of the second part. if (mBiggestAngle < angle) { mBiggestAngle = angle; + mFirstLength = mLength; mFirstAngleVariance = getAnglesVariance(mSumSquares, mSum, mCount); mSecondSumSquares = 0.0f; mSecondSum = 0.0f; @@ -132,9 +166,19 @@ public class AnglesVarianceClassifier extends StrokeClassifier { } public float getAnglesVariance() { - return Math.min(getAnglesVariance(mSumSquares, mSum, mCount), - mFirstAngleVariance + getAnglesVariance(mSecondSumSquares, mSecondSum, - mSecondCount)); + float anglesVariance = getAnglesVariance(mSumSquares, mSum, mCount); + if (mFirstLength < mLength / 2f) { + anglesVariance = Math.min(anglesVariance, mFirstAngleVariance + + getAnglesVariance(mSecondSumSquares, mSecondSum, mSecondCount)); + } + return anglesVariance; + } + + public float getAnglesPercentage() { + if (mAnglesCount == 0.0f) { + return 1.0f; + } + return (Math.max(mLeftAngles, mRightAngles) + mStraightAngles) / mAnglesCount; } } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java new file mode 100644 index 000000000000..a0ceb2958c33 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/AnglesPercentageEvaluator.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.classifier; + +public class AnglesPercentageEvaluator { + public static float evaluate(float value) { + float evaluation = 0.0f; + if (value < 1.00) evaluation++; + if (value < 0.95) evaluation++; + if (value < 0.90) evaluation++; + return evaluation; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java new file mode 100644 index 000000000000..299d0e3634b5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/DirectionClassifier.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.classifier; + +/** + * A classifier which looks at the general direction of a stroke and evaluates it depending on + * the type of action that takes place. + */ +public class DirectionClassifier extends StrokeClassifier { + public DirectionClassifier(ClassifierData classifierData) { + } + + @Override + public float getFalseTouchEvaluation(int type, Stroke stroke) { + Point firstPoint = stroke.getPoints().get(0); + Point lastPoint = stroke.getPoints().get(stroke.getPoints().size() - 1); + return DirectionEvaluator.evaluate(lastPoint.x - firstPoint.x, lastPoint.y - firstPoint.y, + type); + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java new file mode 100644 index 000000000000..e20b1ca64580 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/DirectionEvaluator.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.classifier; + +public class DirectionEvaluator { + public static float evaluate(float xDiff, float yDiff, int type) { + float falsingEvaluation = 5.5f; + boolean vertical = Math.abs(yDiff) >= Math.abs(xDiff); + switch (type) { + case Classifier.QUICK_SETTINGS: + case Classifier.NOTIFICATION_DRAG_DOWN: + if (!vertical || yDiff <= 0.0) { + return falsingEvaluation; + } + break; + case Classifier.NOTIFICATION_DISMISS: + if (vertical) { + return falsingEvaluation; + } + break; + case Classifier.UNLOCK: + if (!vertical || yDiff >= 0.0) { + return falsingEvaluation; + } + break; + case Classifier.LEFT_AFFORDANCE: + if (xDiff < 0.0 && yDiff > 0.0) { + return falsingEvaluation; + } + break; + case Classifier.RIGHT_AFFORDANCE: + if (xDiff > 0.0 && yDiff > 0.0) { + return falsingEvaluation; + } + default: + break; + } + return 0.0f; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java index 0e45ac19469a..a7a569419624 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/HumanInteractionClassifier.java @@ -69,14 +69,15 @@ public class HumanInteractionClassifier extends Classifier { mClassifierData = new ClassifierData(mDpi); mHistoryEvaluator = new HistoryEvaluator(); - mStrokeClassifiers.add(new AnglesVarianceClassifier(mClassifierData)); + mStrokeClassifiers.add(new AnglesClassifier(mClassifierData)); mStrokeClassifiers.add(new SpeedClassifier(mClassifierData)); mStrokeClassifiers.add(new DurationCountClassifier(mClassifierData)); mStrokeClassifiers.add(new EndPointRatioClassifier(mClassifierData)); mStrokeClassifiers.add(new EndPointLengthClassifier(mClassifierData)); mStrokeClassifiers.add(new AccelerationClassifier(mClassifierData)); - mStrokeClassifiers.add(new SpeedVarianceClassifier(mClassifierData)); + mStrokeClassifiers.add(new SpeedAnglesClassifier(mClassifierData)); mStrokeClassifiers.add(new LengthCountClassifier(mClassifierData)); + mStrokeClassifiers.add(new DirectionClassifier(mClassifierData)); mGestureClassifiers.add(new PointerCountClassifier(mClassifierData)); mGestureClassifiers.add(new ProximityClassifier(mClassifierData)); diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java index 1ea467bfb857..cedf4676beec 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountClassifier.java @@ -17,8 +17,11 @@ package com.android.systemui.classifier; /** - * A classifier which looks at the ratio between the duration of the stroke and its number of - * points. + * A classifier which looks at the ratio between the length of the stroke and its number of + * points. The number of points is subtracted by 2 because the UP event comes in with some delay + * and it should not influence the ratio and also strokes which are long and have a small number + * of points are punished more (these kind of strokes are usually bad ones and they tend to score + * well in other classifiers). */ public class LengthCountClassifier extends StrokeClassifier { public LengthCountClassifier(ClassifierData classifierData) { @@ -26,6 +29,7 @@ public class LengthCountClassifier extends StrokeClassifier { @Override public float getFalseTouchEvaluation(int type, Stroke stroke) { - return LengthCountEvaluator.evaluate(stroke.getTotalLength() / stroke.getCount()); + return LengthCountEvaluator.evaluate(stroke.getTotalLength() + / Math.max(1.0f, stroke.getCount() - 2)); } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java index 68f163d1916b..dac7a6f720a7 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/LengthCountEvaluator.java @@ -23,10 +23,11 @@ package com.android.systemui.classifier; public class LengthCountEvaluator { public static float evaluate(float value) { float evaluation = 0.0f; - if (value < 0.07) evaluation++; + if (value < 0.09) evaluation++; if (value < 0.05) evaluation++; if (value < 0.02) evaluation++; if (value > 0.6) evaluation++; + if (value > 0.9) evaluation++; if (value > 1.2) evaluation++; return evaluation; } diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java index 9a30fe1a425c..d544a3d68671 100644 --- a/packages/SystemUI/src/com/android/systemui/classifier/SpeedVarianceClassifier.java +++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesClassifier.java @@ -28,14 +28,17 @@ import java.util.List; * A classifier which for each point from a stroke, it creates a point on plane with coordinates * (timeOffsetNano, distanceCoveredUpToThisPoint) (scaled by DURATION_SCALE and LENGTH_SCALE) * and then it calculates the angle variance of these points like the class - * {@link AnglesVarianceClassifier} (without splitting it into two parts). The classifier ignores + * {@link AnglesClassifier} (without splitting it into two parts). The classifier ignores * the last point of a stroke because the UP event comes in with some delay and this ruins the - * smoothness of this curve + * smoothness of this curve. Additionally, the classifier classifies calculates the percentage of + * angles which value is in [PI - ANGLE_DEVIATION, 2* PI) interval. The reason why the classifier + * does that is because the speed of a good stroke is most often increases, so most of these angels + * should be in this interval. */ -public class SpeedVarianceClassifier extends StrokeClassifier { +public class SpeedAnglesClassifier extends StrokeClassifier { private HashMap<Stroke, Data> mStrokeMap = new HashMap<>(); - public SpeedVarianceClassifier(ClassifierData classifierData) { + public SpeedAnglesClassifier(ClassifierData classifierData) { mClassifierData = classifierData; } @@ -64,12 +67,15 @@ public class SpeedVarianceClassifier extends StrokeClassifier { @Override public float getFalseTouchEvaluation(int type, Stroke stroke) { - return SpeedVarianceEvaluator.evaluate(mStrokeMap.get(stroke).getAnglesVariance()); + Data data = mStrokeMap.get(stroke); + return SpeedVarianceEvaluator.evaluate(data.getAnglesVariance()) + + SpeedAnglesPercentageEvaluator.evaluate(data.getAnglesPercentage()); } private static class Data { private final float DURATION_SCALE = 1e8f; private final float LENGTH_SCALE = 1.0f; + private final float ANGLE_DEVIATION = (float) Math.PI / 10.0f; private List<Point> mLastThreePoints = new ArrayList<>(); private Point mPreviousPoint; @@ -78,6 +84,8 @@ public class SpeedVarianceClassifier extends StrokeClassifier { private float mSum; private float mCount; private float mDist; + private float mAnglesCount; + private float mAcceleratingAngles; public Data() { mPreviousPoint = null; @@ -86,6 +94,7 @@ public class SpeedVarianceClassifier extends StrokeClassifier { mSum = 0.0f; mCount = 1.0f; mDist = 0.0f; + mAnglesCount = mAcceleratingAngles = 0.0f; } public void addPoint(Point point) { @@ -108,6 +117,11 @@ public class SpeedVarianceClassifier extends StrokeClassifier { float angle = mLastThreePoints.get(1).getAngle(mLastThreePoints.get(0), mLastThreePoints.get(2)); + mAnglesCount++; + if (angle >= (float) Math.PI - ANGLE_DEVIATION) { + mAcceleratingAngles++; + } + float difference = angle - mPreviousAngle; mSum += difference; mSumSquares += difference * difference; @@ -120,5 +134,12 @@ public class SpeedVarianceClassifier extends StrokeClassifier { public float getAnglesVariance() { return mSumSquares / mCount - (mSum / mCount) * (mSum / mCount); } + + public float getAnglesPercentage() { + if (mAnglesCount == 0.0f) { + return 1.0f; + } + return (mAcceleratingAngles) / mAnglesCount; + } } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java new file mode 100644 index 000000000000..2a45fa36dbc6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/classifier/SpeedAnglesPercentageEvaluator.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.classifier; + +public class SpeedAnglesPercentageEvaluator { + public static float evaluate(float value) { + float evaluation = 0.0f; + if (value < 1.00) evaluation++; + if (value < 0.95) evaluation++; + if (value < 0.90) evaluation++; + return evaluation; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index a3e89f2f06c4..3e23ed8029cf 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -38,7 +38,6 @@ import android.util.MutableBoolean; import android.view.Display; import android.view.LayoutInflater; import android.view.View; - import com.android.internal.logging.MetricsLogger; import com.android.systemui.Prefs; import com.android.systemui.R; @@ -500,9 +499,8 @@ public class Recents extends SystemUI mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); mNavBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_height); mNavBarWidth = res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width); - // TODO: We can't rely on this anymore since the activity context will yield different - // resources while multiwindow is enabled - mConfig = RecentsConfiguration.reinitialize(mContext, mSystemServicesProxy); + mConfig = RecentsConfiguration.initialize(mContext, mSystemServicesProxy); + mConfig.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect()); mConfig.updateOnConfigurationChange(); Rect searchBarBounds = new Rect(); // Try and pre-emptively bind the search widget on startup to ensure that we @@ -515,7 +513,7 @@ public class Recents extends SystemUI mConfig.getAvailableTaskStackBounds(windowRect, mStatusBarHeight, (mConfig.hasTransposedNavBar ? mNavBarWidth : 0), searchBarBounds, mTaskStackBounds); - if (mConfig.isLandscape && mConfig.hasTransposedNavBar) { + if (mConfig.hasTransposedNavBar) { mSystemInsets.set(0, mStatusBarHeight, mNavBarWidth, 0); } else { mSystemInsets.set(0, mStatusBarHeight, 0, mNavBarHeight); @@ -740,7 +738,7 @@ public class Recents extends SystemUI // Don't reinitialize the configuration completely here, since it has the wrong context, // only update the parts that we can get from any context RecentsConfiguration config = RecentsConfiguration.getInstance(); - config.reinitializeWithApplicationContext(mContext, mSystemServicesProxy); + config.update(mContext, mSystemServicesProxy, mSystemServicesProxy.getWindowRect()); if (sInstanceLoadPlan == null) { // Create a new load plan if onPreloadRecents() was never triggered @@ -816,15 +814,16 @@ public class Recents extends SystemUI ActivityOptions opts, boolean fromHome, boolean fromSearchHome, boolean fromThumbnail, TaskStackViewLayoutAlgorithm.VisibilityReport vr) { // Update the configuration based on the launch options - mConfig.launchedFromHome = fromSearchHome || fromHome; - mConfig.launchedFromSearchHome = fromSearchHome; - mConfig.launchedFromAppWithThumbnail = fromThumbnail; - mConfig.launchedToTaskId = (topTask != null) ? topTask.id : -1; - mConfig.launchedWithAltTab = mTriggeredFromAltTab; - mConfig.launchedReuseTaskStackViews = mCanReuseTaskStackViews; - mConfig.launchedNumVisibleTasks = vr.numVisibleTasks; - mConfig.launchedNumVisibleThumbnails = vr.numVisibleThumbnails; - mConfig.launchedHasConfigurationChanged = false; + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); + launchState.launchedFromHome = fromSearchHome || fromHome; + launchState.launchedFromSearchHome = fromSearchHome; + launchState.launchedFromAppWithThumbnail = fromThumbnail; + launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1; + launchState.launchedWithAltTab = mTriggeredFromAltTab; + launchState.launchedReuseTaskStackViews = mCanReuseTaskStackViews; + launchState.launchedNumVisibleTasks = vr.numVisibleTasks; + launchState.launchedNumVisibleThumbnails = vr.numVisibleThumbnails; + launchState.launchedHasConfigurationChanged = false; Intent intent = new Intent(sToggleRecentsAction); intent.setClassName(sRecentsPackage, sRecentsActivity); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index c53e57325c3d..9ce6b2c764b4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -25,17 +25,13 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.Configuration; import android.os.Bundle; import android.os.SystemClock; import android.os.UserHandle; import android.view.KeyEvent; import android.view.View; import android.view.ViewStub; -import android.widget.Toast; - import com.android.internal.logging.MetricsLogger; -import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.recents.misc.Console; import com.android.systemui.recents.misc.ReferenceCountedTrigger; @@ -182,18 +178,19 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } // Start loading tasks according to the load plan + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); if (!plan.hasTasks()) { - loader.preloadTasks(plan, mConfig.launchedFromHome); + loader.preloadTasks(plan, launchState.launchedFromHome); } RecentsTaskLoadPlan.Options loadOpts = new RecentsTaskLoadPlan.Options(); - loadOpts.runningTaskId = mConfig.launchedToTaskId; - loadOpts.numVisibleTasks = mConfig.launchedNumVisibleTasks; - loadOpts.numVisibleTaskThumbnails = mConfig.launchedNumVisibleThumbnails; + loadOpts.runningTaskId = launchState.launchedToTaskId; + loadOpts.numVisibleTasks = launchState.launchedNumVisibleTasks; + loadOpts.numVisibleTaskThumbnails = launchState.launchedNumVisibleThumbnails; loader.loadTasks(this, plan, loadOpts); TaskStack stack = plan.getTaskStack(); - mConfig.launchedWithNoRecentTasks = !plan.hasTasks(); - if (!mConfig.launchedWithNoRecentTasks) { + launchState.launchedWithNoRecentTasks = !plan.hasTasks(); + if (!launchState.launchedWithNoRecentTasks) { mRecentsView.setTaskStack(stack); } @@ -204,19 +201,19 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent, ActivityOptions.makeCustomAnimation(this, - mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter : + launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_enter : R.anim.recents_to_launcher_enter, - mConfig.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit : + launchState.launchedFromSearchHome ? R.anim.recents_to_search_launcher_exit : R.anim.recents_to_launcher_exit)); // Mark the task that is the launch target int launchTaskIndexInStack = 0; - if (mConfig.launchedToTaskId != -1) { + if (launchState.launchedToTaskId != -1) { ArrayList<Task> tasks = stack.getTasks(); int taskCount = tasks.size(); for (int j = 0; j < taskCount; j++) { Task t = tasks.get(j); - if (t.key.id == mConfig.launchedToTaskId) { + if (t.key.id == launchState.launchedToTaskId) { t.isLaunchTarget = true; launchTaskIndexInStack = tasks.size() - j - 1; break; @@ -225,7 +222,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView } // Update the top level view's visibilities - if (mConfig.launchedWithNoRecentTasks) { + if (launchState.launchedWithNoRecentTasks) { if (mEmptyView == null) { mEmptyView = mEmptyViewStub.inflate(); } @@ -246,13 +243,13 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView mScrimViews.prepareEnterRecentsAnimation(); // Keep track of whether we launched from the nav bar button or via alt-tab - if (mConfig.launchedWithAltTab) { + if (launchState.launchedWithAltTab) { MetricsLogger.count(this, "overview_trigger_alttab", 1); } else { MetricsLogger.count(this, "overview_trigger_nav_btn", 1); } // Keep track of whether we launched from an app or from home - if (mConfig.launchedFromAppWithThumbnail) { + if (launchState.launchedFromAppWithThumbnail) { MetricsLogger.count(this, "overview_source_app", 1); // If from an app, track the stack index of the app in the stack (for affiliated tasks) MetricsLogger.histogram(this, "overview_source_app_index", launchTaskIndexInStack); @@ -266,6 +263,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView /** Dismisses recents if we are already visible and the intent is to toggle the recents view */ boolean dismissRecentsToFocusedTaskOrHome(boolean checkFilteredStackState) { + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); if (ssp.isRecentsTopMost(ssp.getTopMostTask(), null)) { // If we currently have filtered stacks, then unfilter those first @@ -274,7 +272,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // If we have a focused Task, launch that Task now if (mRecentsView.launchFocusedTask()) return true; // If we launched from Home, then return to Home - if (mConfig.launchedFromHome) { + if (launchState.launchedFromHome) { dismissRecentsToHomeRaw(true); return true; } @@ -324,7 +322,8 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // initialized RecentsTaskLoader.initialize(this); SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy(); - mConfig = RecentsConfiguration.reinitialize(this, ssp); + mConfig = RecentsConfiguration.initialize(this, ssp); + mConfig.update(this, ssp, ssp.getWindowRect()); // Initialize the widget host (the host id is static and does not change) mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId); @@ -337,7 +336,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); mEmptyViewStub = (ViewStub) findViewById(R.id.empty_view_stub); - mScrimViews = new SystemBarScrimViews(this, mConfig); + mScrimViews = new SystemBarScrimViews(this); // Bind the search app widget when we first start up mSearchWidgetInfo = ssp.getOrBindSearchAppWidget(this, mAppWidgetHost); @@ -358,6 +357,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override protected void onStart() { super.onStart(); + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); MetricsLogger.visible(this, MetricsLogger.OVERVIEW_ACTIVITY); RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); SystemServicesProxy ssp = loader.getSystemServicesProxy(); @@ -379,12 +379,13 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // If this is a new instance from a configuration change, then we have to manually trigger // the enter animation state, or if recents was relaunched by AM, without going through // the normal mechanisms - boolean wasLaunchedByAm = !mConfig.launchedFromHome && !mConfig.launchedFromAppWithThumbnail; - if (mConfig.launchedHasConfigurationChanged || wasLaunchedByAm) { + boolean wasLaunchedByAm = !launchState.launchedFromHome && + !launchState.launchedFromAppWithThumbnail; + if (launchState.launchedHasConfigurationChanged || wasLaunchedByAm) { onEnterAnimationTriggered(); } - if (!mConfig.launchedHasConfigurationChanged) { + if (!launchState.launchedHasConfigurationChanged) { mRecentsView.disableLayersForOneFrame(); } } @@ -402,6 +403,7 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView protected void onStop() { super.onStop(); MetricsLogger.hidden(this, MetricsLogger.OVERVIEW_ACTIVITY); + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); RecentsTaskLoader loader = RecentsTaskLoader.getInstance(); SystemServicesProxy ssp = loader.getSystemServicesProxy(); Recents.notifyVisibilityChanged(this, ssp, false); @@ -418,12 +420,12 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView // Workaround for b/22542869, if the RecentsActivity is started again, but without going // through SystemUI, we need to reset the config launch flags to ensure that we do not // wait on the system to send a signal that was never queued. - mConfig.launchedFromHome = false; - mConfig.launchedFromSearchHome = false; - mConfig.launchedFromAppWithThumbnail = false; - mConfig.launchedToTaskId = -1; - mConfig.launchedWithAltTab = false; - mConfig.launchedHasConfigurationChanged = false; + launchState.launchedFromHome = false; + launchState.launchedFromSearchHome = false; + launchState.launchedFromAppWithThumbnail = false; + launchState.launchedToTaskId = -1; + launchState.launchedWithAltTab = false; + launchState.launchedHasConfigurationChanged = false; } @Override @@ -475,8 +477,9 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_TAB: { + int altTabKeyDelay = getResources().getInteger(R.integer.recents_alt_tab_key_delay); boolean hasRepKeyTimeElapsed = (SystemClock.elapsedRealtime() - - mLastTabKeyEventTime) > mConfig.altTabKeyDelay; + mLastTabKeyEventTime) > altTabKeyDelay; if (event.getRepeatCount() <= 0 || hasRepKeyTimeElapsed) { // Focus the next task in the stack final boolean backward = event.isShiftPressed(); @@ -514,9 +517,6 @@ public class RecentsActivity extends Activity implements RecentsView.RecentsView @Override public void onBackPressed() { - // Test mode where back does not do anything - if (mConfig.debugModeEnabled) return; - // Dismiss Recents to the focused Task or Home dismissRecentsToFocusedTaskOrHome(true); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java new file mode 100644 index 000000000000..e2e0e918ab51 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.recents; + +/** + * The launch state of the RecentsActivity. + * + * TODO: We will be refactoring this out RecentsConfiguration. + * Current Constraints: + * - needed in onStart() before onNewIntent() + * - needs to be reset when Recents is hidden + * - needs to be computed in Recents component + * - needs to be accessible by views + */ +public class RecentsActivityLaunchState { + + public RecentsConfiguration mConfig; + + public boolean launchedWithAltTab; + public boolean launchedWithNoRecentTasks; + public boolean launchedFromAppWithThumbnail; + public boolean launchedFromHome; + public boolean launchedFromSearchHome; + public boolean launchedReuseTaskStackViews; + public boolean launchedHasConfigurationChanged; + public int launchedToTaskId; + public int launchedNumVisibleTasks; + public int launchedNumVisibleThumbnails; + + RecentsActivityLaunchState(RecentsConfiguration config) { + mConfig = config; + } + + /** Called when the configuration has changed, and we want to reset any configuration specific + * members. */ + public void updateOnConfigurationChange() { + // Reset this flag on configuration change to ensure that we recreate new task views + launchedReuseTaskStackViews = false; + // Set this flag to indicate that the configuration has changed since Recents last launched + launchedHasConfigurationChanged = true; + } + + /** Returns whether the status bar scrim should be animated when shown for the first time. */ + public boolean shouldAnimateStatusBarScrim() { + return launchedFromHome; + } + + /** Returns whether the status bar scrim should be visible. */ + public boolean hasStatusBarScrim() { + return !launchedWithNoRecentTasks; + } + + /** Returns whether the nav bar scrim should be animated when shown for the first time. */ + public boolean shouldAnimateNavBarScrim() { + return true; + } + + /** Returns whether the nav bar scrim should be visible. */ + public boolean hasNavBarScrim() { + // Only show the scrim if we have recent tasks, and if the nav bar is not transposed + return !launchedWithNoRecentTasks && mConfig.hasTransposedNavBar; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index a59eb3048be3..52b9521b9952 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -16,27 +16,29 @@ package com.android.systemui.recents; -import android.app.ActivityManager; import android.content.Context; -import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.provider.Settings; -import android.util.DisplayMetrics; -import android.view.animation.AnimationUtils; -import android.view.animation.Interpolator; - -import com.android.systemui.Prefs; import com.android.systemui.R; -import com.android.systemui.recents.misc.Console; import com.android.systemui.recents.misc.SystemServicesProxy; - -/** A static Recents configuration for the current context - * NOTE: We should not hold any references to a Context from a static instance */ +/** + * Application resources that can be retrieved from the application context and are not specifically + * tied to the current activity. + */ public class RecentsConfiguration { static RecentsConfiguration sInstance; - static int sPrevConfigurationHashCode; + + private static final int LARGE_SCREEN_MIN_DP = 600; + private static final int XLARGE_SCREEN_MIN_DP = 720; + + // Variables that are used for global calculations + private static final float STACK_SIDE_PADDING_PHONES_PCT = 0.03333f; + private static final float STACK_SIZE_PADDING_TABLETS_PCT = 0.075f; + private static final float STACK_SIZE_PADDING_LARGE_TABLETS_PCT = 0.15f; + private static final int SEARCH_BAR_SPACE_HEIGHT_PHONES_DPS = 64; + private static final int SEARCH_BAR_SPACE_HEIGHT_TABLETS_DPS = 72; /** Levels of svelte in increasing severity/austerity. */ // No svelting. @@ -50,123 +52,81 @@ public class RecentsConfiguration { // Disable all thumbnail loading. public static final int SVELTE_DISABLE_LOADING = 3; - /** Interpolators */ - public Interpolator fastOutSlowInInterpolator; - public Interpolator fastOutLinearInInterpolator; - public Interpolator linearOutSlowInInterpolator; - public Interpolator quintOutInterpolator; + // Launch states + public RecentsActivityLaunchState mLaunchState = new RecentsActivityLaunchState(this); - /** Filtering */ - public int filteringCurrentViewsAnimDuration; - public int filteringNewViewsAnimDuration; - - /** Insets */ - public Rect systemInsets = new Rect(); - public Rect displayRect = new Rect(); - - /** Layout */ - boolean isLandscape; + // TODO: Values determined by the current context, needs to be refactored into something that is + // agnostic of the activity context, but still calculable from the Recents component for + // the transition into recents boolean hasTransposedSearchBar; boolean hasTransposedNavBar; - - /** Loading */ - public int maxNumTasksToLoad; - - /** Search bar */ - public int searchBarSpaceHeightPx; - - /** Task stack */ - public int taskStackScrollDuration; - public int taskStackMaxDim; - public int taskStackTopPaddingPx; - public int dismissAllButtonSizePx; public float taskStackWidthPaddingPct; - public float taskStackOverscrollPct; - - /** Transitions */ - public int transitionEnterFromAppDelay; - public int transitionEnterFromHomeDelay; - - /** Task view animation and styles */ - public int taskViewEnterFromAppDuration; - public int taskViewEnterFromHomeDuration; - public int taskViewEnterFromHomeStaggerDelay; - public int taskViewExitToAppDuration; - public int taskViewExitToHomeDuration; - public int taskViewRemoveAnimDuration; - public int taskViewRemoveAnimTranslationXPx; - public int taskViewTranslationZMinPx; - public int taskViewTranslationZMaxPx; - public int taskViewRoundedCornerRadiusPx; - public int taskViewHighlightPx; - public int taskViewAffiliateGroupEnterOffsetPx; - public float taskViewThumbnailAlpha; - - /** Task bar colors */ - public int taskBarViewDefaultBackgroundColor; - public int taskBarViewLightTextColor; - public int taskBarViewDarkTextColor; - public int taskBarViewHighlightColor; - public float taskBarViewAffiliationColorMinAlpha; - /** Task bar size & animations */ - public int taskBarHeight; - public int taskBarDismissDozeDelaySeconds; - - /** Nav bar scrim */ - public int navBarScrimEnterDuration; - - /** Launch states */ - public boolean launchedWithAltTab; - public boolean launchedWithNoRecentTasks; - public boolean launchedFromAppWithThumbnail; - public boolean launchedFromHome; - public boolean launchedFromSearchHome; - public boolean launchedReuseTaskStackViews; - public boolean launchedHasConfigurationChanged; - public int launchedToTaskId; - public int launchedNumVisibleTasks; - public int launchedNumVisibleThumbnails; + // Since the positions in Recents has to be calculated globally (before the RecentsActivity + // starts), we need to calculate some resource values ourselves, instead of relying on framework + // resources. + public final boolean isLargeScreen; + public final boolean isXLargeScreen; + public final int smallestWidth; /** Misc **/ public boolean useHardwareLayers; - public int altTabKeyDelay; public boolean fakeShadows; + public int svelteLevel; + public int searchBarSpaceHeightPx; /** Dev options and global settings */ public boolean multiWindowEnabled; public boolean lockToAppEnabled; - public boolean developerOptionsEnabled; - public boolean debugModeEnabled; - public int svelteLevel; /** Private constructor */ - private RecentsConfiguration(Context context) { - // Properties that don't have to be reloaded with each configuration change can be loaded - // here. + private RecentsConfiguration(Context context, SystemServicesProxy ssp) { + // Load only resources that can not change after the first load either through developer + // settings or via multi window + Context appContext = context.getApplicationContext(); + Resources res = appContext.getResources(); + useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers); + fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows); + svelteLevel = res.getInteger(R.integer.recents_svelte_level); + + float density = context.getResources().getDisplayMetrics().density; + smallestWidth = ssp.getDeviceSmallestWidth(); + isLargeScreen = smallestWidth >= (int) (density * LARGE_SCREEN_MIN_DP); + isXLargeScreen = smallestWidth >= (int) (density * XLARGE_SCREEN_MIN_DP); + searchBarSpaceHeightPx = isLargeScreen ? + (int) (density * SEARCH_BAR_SPACE_HEIGHT_TABLETS_DPS) : + (int) (density * SEARCH_BAR_SPACE_HEIGHT_PHONES_DPS); + if (isLargeScreen) { + taskStackWidthPaddingPct = STACK_SIZE_PADDING_TABLETS_PCT; + } else if (isXLargeScreen) { + taskStackWidthPaddingPct = STACK_SIZE_PADDING_LARGE_TABLETS_PCT; + } else { + taskStackWidthPaddingPct = STACK_SIDE_PADDING_PHONES_PCT; + } + } + + /** + * Updates the configuration based on the current state of the system + */ + void update(Context context, SystemServicesProxy ssp, Rect windowRect) { + // Only update resources that can change after the first load, either through developer + // settings or via multi window + lockToAppEnabled = ssp.getSystemSetting(context, + Settings.System.LOCK_TO_APP_ENABLED) != 0; + multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window")); - // Interpolators - fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.fast_out_slow_in); - fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.fast_out_linear_in); - linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.linear_out_slow_in); - quintOutInterpolator = AnimationUtils.loadInterpolator(context, - com.android.internal.R.interpolator.decelerate_quint); + // Recompute some values based on the given state, since we can not rely on the resource + // system to get certain values. + boolean isLandscape = windowRect.width() > windowRect.height(); + hasTransposedNavBar = isLandscape && isLargeScreen && !isXLargeScreen; + hasTransposedSearchBar = isLandscape && isLargeScreen && !isXLargeScreen; } /** Updates the configuration to the current context */ - public static RecentsConfiguration reinitialize(Context context, SystemServicesProxy ssp) { + public static RecentsConfiguration initialize(Context context, SystemServicesProxy ssp) { if (sInstance == null) { - sInstance = new RecentsConfiguration(context); + sInstance = new RecentsConfiguration(context, ssp); } - int configHashCode = context.getResources().getConfiguration().hashCode(); - if (sPrevConfigurationHashCode != configHashCode) { - sInstance.update(context); - sPrevConfigurationHashCode = configHashCode; - } - sInstance.reinitializeWithApplicationContext(context.getApplicationContext(), ssp); return sInstance; } @@ -175,145 +135,18 @@ public class RecentsConfiguration { return sInstance; } - /** Updates the state, given the specified context */ - void update(Context context) { - Resources res = context.getResources(); - DisplayMetrics dm = res.getDisplayMetrics(); - - // Debug mode - debugModeEnabled = Prefs.getBoolean(context, Prefs.Key.DEBUG_MODE_ENABLED, - false /* defaultValue */); - if (debugModeEnabled) { - Console.Enabled = true; - } - - // Layout - isLandscape = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE; - hasTransposedSearchBar = res.getBoolean(R.bool.recents_has_transposed_search_bar); - hasTransposedNavBar = res.getBoolean(R.bool.recents_has_transposed_nav_bar); - - // Insets - displayRect.set(0, 0, dm.widthPixels, dm.heightPixels); - - // Filtering - filteringCurrentViewsAnimDuration = - res.getInteger(R.integer.recents_filter_animate_current_views_duration); - filteringNewViewsAnimDuration = - res.getInteger(R.integer.recents_filter_animate_new_views_duration); - - // Loading - maxNumTasksToLoad = ActivityManager.getMaxRecentTasksStatic(); - - // Search Bar - searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height); - - // Task stack - taskStackScrollDuration = - res.getInteger(R.integer.recents_animate_task_stack_scroll_duration); - taskStackWidthPaddingPct = res.getFloat(R.dimen.recents_stack_width_padding_percentage); - taskStackOverscrollPct = res.getFloat(R.dimen.recents_stack_overscroll_percentage); - taskStackMaxDim = res.getInteger(R.integer.recents_max_task_stack_view_dim); - taskStackTopPaddingPx = res.getDimensionPixelSize(R.dimen.recents_stack_top_padding); - dismissAllButtonSizePx = res.getDimensionPixelSize(R.dimen.recents_dismiss_all_button_size); - - // Transition - transitionEnterFromAppDelay = - res.getInteger(R.integer.recents_enter_from_app_transition_duration); - transitionEnterFromHomeDelay = - res.getInteger(R.integer.recents_enter_from_home_transition_duration); - - // Task view animation and styles - taskViewEnterFromAppDuration = - res.getInteger(R.integer.recents_task_enter_from_app_duration); - taskViewEnterFromHomeDuration = - res.getInteger(R.integer.recents_task_enter_from_home_duration); - taskViewEnterFromHomeStaggerDelay = - res.getInteger(R.integer.recents_task_enter_from_home_stagger_delay); - taskViewExitToAppDuration = - res.getInteger(R.integer.recents_task_exit_to_app_duration); - taskViewExitToHomeDuration = - res.getInteger(R.integer.recents_task_exit_to_home_duration); - taskViewRemoveAnimDuration = - res.getInteger(R.integer.recents_animate_task_view_remove_duration); - taskViewRemoveAnimTranslationXPx = - res.getDimensionPixelSize(R.dimen.recents_task_view_remove_anim_translation_x); - taskViewRoundedCornerRadiusPx = - res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius); - taskViewHighlightPx = res.getDimensionPixelSize(R.dimen.recents_task_view_highlight); - taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min); - taskViewTranslationZMaxPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_max); - taskViewAffiliateGroupEnterOffsetPx = - res.getDimensionPixelSize(R.dimen.recents_task_view_affiliate_group_enter_offset); - taskViewThumbnailAlpha = res.getFloat(R.dimen.recents_task_view_thumbnail_alpha); - - // Task bar colors - taskBarViewDefaultBackgroundColor = context.getColor( - R.color.recents_task_bar_default_background_color); - taskBarViewLightTextColor = context.getColor(R.color.recents_task_bar_light_text_color); - taskBarViewDarkTextColor = context.getColor(R.color.recents_task_bar_dark_text_color); - taskBarViewHighlightColor = context.getColor(R.color.recents_task_bar_highlight_color); - taskBarViewAffiliationColorMinAlpha = res.getFloat( - R.dimen.recents_task_affiliation_color_min_alpha_percentage); - - // Task bar size & animations - taskBarHeight = res.getDimensionPixelSize(R.dimen.recents_task_bar_height); - taskBarDismissDozeDelaySeconds = - res.getInteger(R.integer.recents_task_bar_dismiss_delay_seconds); - - // Nav bar scrim - navBarScrimEnterDuration = - res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration); - - // Misc - useHardwareLayers = res.getBoolean(R.bool.config_recents_use_hardware_layers); - altTabKeyDelay = res.getInteger(R.integer.recents_alt_tab_key_delay); - fakeShadows = res.getBoolean(R.bool.config_recents_fake_shadows); - svelteLevel = res.getInteger(R.integer.recents_svelte_level); - } - - /** Updates the system insets */ - public void updateSystemInsets(Rect insets) { - systemInsets.set(insets); - } - - /** Updates the states that need to be re-read from the application context. */ - void reinitializeWithApplicationContext(Context context, SystemServicesProxy ssp) { - // Check if the developer options are enabled - developerOptionsEnabled = ssp.getGlobalSetting(context, - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED) != 0; - lockToAppEnabled = ssp.getSystemSetting(context, - Settings.System.LOCK_TO_APP_ENABLED) != 0; - multiWindowEnabled = "true".equals(ssp.getSystemProperty("persist.sys.debug.multi_window")); + /** + * Returns the activity launch state. + * TODO: This will be refactored out of RecentsConfiguration. + */ + public RecentsActivityLaunchState getLaunchState() { + return mLaunchState; } /** Called when the configuration has changed, and we want to reset any configuration specific * members. */ public void updateOnConfigurationChange() { - // Reset this flag on configuration change to ensure that we recreate new task views - launchedReuseTaskStackViews = false; - // Set this flag to indicate that the configuration has changed since Recents last launched - launchedHasConfigurationChanged = true; - } - - /** Returns whether the status bar scrim should be animated when shown for the first time. */ - public boolean shouldAnimateStatusBarScrim() { - return launchedFromHome; - } - - /** Returns whether the status bar scrim should be visible. */ - public boolean hasStatusBarScrim() { - return !launchedWithNoRecentTasks; - } - - /** Returns whether the nav bar scrim should be animated when shown for the first time. */ - public boolean shouldAnimateNavBarScrim() { - return true; - } - - /** Returns whether the nav bar scrim should be visible. */ - public boolean hasNavBarScrim() { - // Only show the scrim if we have recent tasks, and if the nav bar is not transposed - return !launchedWithNoRecentTasks && (!hasTransposedNavBar || !isLandscape); + mLaunchState.updateOnConfigurationChange(); } /** @@ -322,14 +155,17 @@ public class RecentsConfiguration { */ public void getAvailableTaskStackBounds(Rect windowBounds, int topInset, int rightInset, Rect searchBarBounds, Rect taskStackBounds) { - if (isLandscape && hasTransposedSearchBar) { - // In landscape, the search bar appears on the left, but we overlay it on top - taskStackBounds.set(windowBounds.left, windowBounds.top + topInset, - windowBounds.right - rightInset, windowBounds.bottom); + if (hasTransposedNavBar) { + // In landscape phones, the search bar appears on the left, but we overlay it on top + int swInset = getInsetToSmallestWidth(windowBounds.right - rightInset - + windowBounds.left); + taskStackBounds.set(windowBounds.left + swInset, windowBounds.top + topInset, + windowBounds.right - swInset - rightInset, windowBounds.bottom); } else { // In portrait, the search bar appears on the top (which already has the inset) - taskStackBounds.set(windowBounds.left, searchBarBounds.bottom, - windowBounds.right, windowBounds.bottom); + int swInset = getInsetToSmallestWidth(windowBounds.right - windowBounds.left); + taskStackBounds.set(windowBounds.left + swInset, searchBarBounds.bottom, + windowBounds.right - swInset, windowBounds.bottom); } } @@ -340,8 +176,8 @@ public class RecentsConfiguration { public void getSearchBarBounds(Rect windowBounds, int topInset, Rect searchBarSpaceBounds) { // Return empty rects if search is not enabled int searchBarSize = searchBarSpaceHeightPx; - if (isLandscape && hasTransposedSearchBar) { - // In landscape, the search bar appears on the left + if (hasTransposedSearchBar) { + // In landscape phones, the search bar appears on the left searchBarSpaceBounds.set(windowBounds.left, windowBounds.top + topInset, windowBounds.left + searchBarSize, windowBounds.bottom); } else { @@ -350,4 +186,14 @@ public class RecentsConfiguration { windowBounds.right, windowBounds.top + topInset + searchBarSize); } } + + /** + * Constrain the width of the landscape stack to the smallest width of the device. + */ + private int getInsetToSmallestWidth(int availableWidth) { + if (availableWidth > smallestWidth) { + return (availableWidth - smallestWidth) / 2; + } + return 0; + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java index 85f5b5d4d18b..59df293f71d7 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsResizeTaskDialog.java @@ -21,7 +21,6 @@ import android.app.AlertDialog; import android.app.Dialog; import android.app.DialogFragment; import android.app.FragmentManager; -import android.content.Context; import android.content.DialogInterface; import android.graphics.Rect; import android.os.Bundle; diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index cbf5c058d102..231843ea2690 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -39,7 +39,6 @@ import android.widget.Button; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; - import com.android.systemui.R; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index a2d7d0108362..b6d25f507cb9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -32,7 +32,6 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.PackageParser; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Bitmap; @@ -56,18 +55,18 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Log; import android.util.MutableBoolean; +import android.util.MutableFloat; +import android.util.MutableInt; import android.util.Pair; -import android.util.SparseArray; +import android.util.Size; import android.view.Display; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; - import com.android.internal.app.AssistUtils; import com.android.systemui.Prefs; import com.android.systemui.R; import com.android.systemui.recents.Constants; import com.android.systemui.recents.Recents; -import com.android.systemui.recents.RecentsConfiguration; import java.io.IOException; import java.util.ArrayList; @@ -662,6 +661,18 @@ public class SystemServicesProxy { } /** + * Returns the smallest width/height. + */ + public int getDeviceSmallestWidth() { + if (mWm == null) return 0; + + Point smallestSizeRange = new Point(); + Point largestSizeRange = new Point(); + mWm.getDefaultDisplay().getCurrentSizeRange(smallestSizeRange, largestSizeRange); + return smallestSizeRange.x; + } + + /** * Returns the display rect. */ public Rect getDisplayRect() { @@ -675,7 +686,7 @@ public class SystemServicesProxy { } /** - * Returns the window rect. + * Returns the window rect for the RecentsActivity, based on the dimensions of the home stack. */ public Rect getWindowRect() { Rect windowRect = new Rect(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index 649cb4db0fb1..6ef725306815 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -23,7 +23,6 @@ import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.util.Log; -import android.util.SparseArray; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.misc.SystemServicesProxy; @@ -76,7 +75,7 @@ public class RecentsTaskLoadPlan { * An optimization to preload the raw list of tasks. */ public synchronized void preloadRawTasks(boolean isTopTaskHome) { - mRawTasks = mSystemServicesProxy.getRecentTasks(mConfig.maxNumTasksToLoad, + mRawTasks = mSystemServicesProxy.getRecentTasks(ActivityManager.getMaxRecentTasksStatic(), UserHandle.CURRENT.getIdentifier(), isTopTaskHome); Collections.reverse(mRawTasks); @@ -125,7 +124,7 @@ public class RecentsTaskLoadPlan { activityLabel, mSystemServicesProxy, res); Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, mSystemServicesProxy, res, infoHandle, false); - int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig); + int activityColor = loader.getActivityPrimaryColor(t.taskDescription, res); // Update the activity info cache if (!hadCachedActivityInfo && infoHandle.info != null) { @@ -153,7 +152,7 @@ public class RecentsTaskLoadPlan { // Initialize the stacks mStack = new TaskStack(); mStack.setTasks(stackTasks); - mStack.createAffiliatedGroupings(mConfig); + mStack.createAffiliatedGroupings(mContext); } /** diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index ad25c85860ed..760382ed469e 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -27,7 +27,6 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; - import com.android.systemui.R; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; @@ -248,7 +247,7 @@ class TaskResourceLoader implements Runnable { } /* Recents task loader - * NOTE: We should not hold any references to a Context from a static instance */ + * NOTE: We should not hold any references to non-application Context from a static instance */ public class RecentsTaskLoader { private static final String TAG = "RecentsTaskLoader"; @@ -438,12 +437,11 @@ public class RecentsTaskLoader { } /** Returns the activity's primary color. */ - public int getActivityPrimaryColor(ActivityManager.TaskDescription td, - RecentsConfiguration config) { + public int getActivityPrimaryColor(ActivityManager.TaskDescription td, Resources res) { if (td != null && td.getPrimaryColor() != 0) { return td.getPrimaryColor(); } - return config.taskBarViewDefaultBackgroundColor; + return res.getColor(R.color.recents_task_bar_default_background_color); } /** Returns the size of the app icon cache. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java index 515e5784412a..20d9203ef71f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java @@ -16,11 +16,12 @@ package com.android.systemui.recents.model; +import android.content.Context; import android.graphics.Color; import com.android.systemui.recents.Constants; -import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.misc.NamedCounter; import com.android.systemui.recents.misc.Utilities; +import com.android.systemui.R; import java.util.ArrayList; import java.util.Collections; @@ -367,7 +368,7 @@ public class TaskStack { /** * Temporary: This method will simulate affiliation groups by */ - public void createAffiliatedGroupings(RecentsConfiguration config) { + public void createAffiliatedGroupings(Context context) { if (Constants.DebugFlags.App.EnableSimulatedTaskGroups) { HashMap<Task.TaskKey, Task> taskMap = new HashMap<Task.TaskKey, Task>(); // Sort all tasks by increasing firstActiveTime of the task @@ -452,7 +453,8 @@ public class TaskStack { tasksMap.put(t.key, t); } // Update the task colors for each of the groups - float minAlpha = config.taskBarViewAffiliationColorMinAlpha; + float minAlpha = context.getResources().getFloat( + R.dimen.recents_task_affiliation_color_min_alpha_percentage); int taskGroupCount = mGroups.size(); for (int i = 0; i < taskGroupCount; i++) { TaskGrouping group = mGroups.get(i); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java index 41adbed8fd3d..682fd8fe1724 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/FakeShadowDrawable.java @@ -89,7 +89,8 @@ class FakeShadowDrawable extends Drawable { mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG); mCornerShadowPaint.setStyle(Paint.Style.FILL); mCornerShadowPaint.setDither(true); - mCornerRadius = config.taskViewRoundedCornerRadiusPx; + mCornerRadius = resources.getDimensionPixelSize( + R.dimen.recents_task_view_rounded_corners_radius); mCardBounds = new RectF(); mEdgeShadowPaint = new Paint(mCornerShadowPaint); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java index 9685a245879c..92ed0f1b1731 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java @@ -38,8 +38,9 @@ import android.view.LayoutInflater; import android.view.View; import android.view.WindowInsets; import android.view.WindowManagerGlobal; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.FrameLayout; - import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.recents.Constants; @@ -83,6 +84,9 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV TaskStackView mTaskStackView; RecentsAppWidgetHostView mSearchBar; RecentsViewCallbacks mCb; + Interpolator mFastOutSlowInInterpolator; + + Rect mSystemInsets = new Rect(); public RecentsView(Context context) { super(context); @@ -100,6 +104,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV super(context, attrs, defStyleAttr, defStyleRes); mConfig = RecentsConfiguration.getInstance(); mInflater = LayoutInflater.from(context); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); } /** Sets the callbacks */ @@ -109,7 +115,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV /** Set/get the bsp root node */ public void setTaskStack(TaskStack stack) { - if (mConfig.launchedReuseTaskStackViews) { + if (mConfig.getLaunchState().launchedReuseTaskStackViews) { if (mTaskStackView != null) { // If onRecentsHidden is not triggered, we need to the stack view again here mTaskStackView.reset(); @@ -278,7 +284,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // Get the search bar bounds and measure the search bar layout Rect searchBarSpaceBounds = new Rect(); if (mSearchBar != null) { - mConfig.getSearchBarBounds(new Rect(0, 0, width, height), mConfig.systemInsets.top, + mConfig.getSearchBarBounds(new Rect(0, 0, width, height), mSystemInsets.top, searchBarSpaceBounds); mSearchBar.measure( MeasureSpec.makeMeasureSpec(searchBarSpaceBounds.width(), MeasureSpec.EXACTLY), @@ -286,8 +292,8 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV } Rect taskStackBounds = new Rect(); - mConfig.getAvailableTaskStackBounds(new Rect(0, 0, width, height), mConfig.systemInsets.top, - mConfig.systemInsets.right, searchBarSpaceBounds, taskStackBounds); + mConfig.getAvailableTaskStackBounds(new Rect(0, 0, width, height), mSystemInsets.top, + mSystemInsets.right, searchBarSpaceBounds, taskStackBounds); if (mTaskStackView != null && mTaskStackView.getVisibility() != GONE) { mTaskStackView.setTaskStackBounds(taskStackBounds); mTaskStackView.measure(widthMeasureSpec, heightMeasureSpec); @@ -306,7 +312,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV Rect searchBarSpaceBounds = new Rect(); if (mSearchBar != null) { mConfig.getSearchBarBounds(measuredRect, - mConfig.systemInsets.top, searchBarSpaceBounds); + mSystemInsets.top, searchBarSpaceBounds); mSearchBar.layout(searchBarSpaceBounds.left, searchBarSpaceBounds.top, searchBarSpaceBounds.right, searchBarSpaceBounds.bottom); } @@ -318,8 +324,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { - // Update the configuration with the latest system insets and trigger a relayout - mConfig.updateSystemInsets(insets.getSystemWindowInsets()); + mSystemInsets.set(insets.getSystemWindowInsets()); requestLayout(); return insets.consumeSystemWindowInsets(); } @@ -548,7 +553,7 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV // outside the display rect (to ensure we don't animate from too far away) sourceView = stackView; offsetX = transform.rect.left; - offsetY = mConfig.displayRect.height(); + offsetY = getMeasuredHeight(); } else { sourceView = tv.mThumbnailView; } @@ -700,11 +705,13 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV public void onTaskStackFilterTriggered() { // Hide the search bar if (mSearchBar != null) { + int filterDuration = getResources().getInteger( + R.integer.recents_filter_animate_current_views_duration); mSearchBar.animate() .alpha(0f) .setStartDelay(0) - .setInterpolator(mConfig.fastOutSlowInInterpolator) - .setDuration(mConfig.filteringCurrentViewsAnimDuration) + .setInterpolator(mFastOutSlowInInterpolator) + .setDuration(filterDuration) .withLayer() .start(); } @@ -714,11 +721,13 @@ public class RecentsView extends FrameLayout implements TaskStackView.TaskStackV public void onTaskStackUnfilterTriggered() { // Show the search bar if (mSearchBar != null) { + int filterDuration = getResources().getInteger( + R.integer.recents_filter_animate_new_views_duration); mSearchBar.animate() .alpha(1f) .setStartDelay(0) - .setInterpolator(mConfig.fastOutSlowInInterpolator) - .setDuration(mConfig.filteringNewViewsAnimDuration) + .setInterpolator(mFastOutSlowInInterpolator) + .setDuration(filterDuration) .withLayer() .start(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java index 0428b48490d6..e04699c10fdd 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/SwipeHelper.java @@ -22,13 +22,15 @@ import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.TargetApi; +import android.content.Context; import android.os.Build; import android.util.DisplayMetrics; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.view.animation.LinearInterpolator; -import com.android.systemui.recents.RecentsConfiguration; /** * This class facilitates swipe to dismiss. It defines an interface to be implemented by the @@ -46,6 +48,7 @@ public class SwipeHelper { public static final int Y = 1; private static LinearInterpolator sLinearInterpolator = new LinearInterpolator(); + private Interpolator mLinearOutSlowInInterpolator; private float SWIPE_ESCAPE_VELOCITY = 100f; // dp/sec private int DEFAULT_ESCAPE_ANIMATION_DURATION = 75; // ms @@ -74,13 +77,15 @@ public class SwipeHelper { public boolean mAllowSwipeTowardsEnd = true; private boolean mRtl; - public SwipeHelper(int swipeDirection, Callback callback, float densityScale, + public SwipeHelper(Context context, int swipeDirection, Callback callback, float densityScale, float pagingTouchSlop) { mCallback = callback; mSwipeDirection = swipeDirection; mVelocityTracker = VelocityTracker.obtain(); mDensityScale = densityScale; mPagingTouchSlop = pagingTouchSlop; + mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.linear_out_slow_in); } public void setDensityScale(float densityScale) { @@ -265,7 +270,7 @@ public class SwipeHelper { ValueAnimator anim = createTranslationAnimation(view, 0); int duration = SNAP_ANIM_LEN; anim.setDuration(duration); - anim.setInterpolator(RecentsConfiguration.getInstance().linearOutSlowInInterpolator); + anim.setInterpolator(mLinearOutSlowInInterpolator); anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java index 1086160e07e7..7ce50d803ee2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java @@ -17,13 +17,19 @@ package com.android.systemui.recents.views; import android.app.Activity; +import android.content.Context; import android.view.View; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import com.android.systemui.R; +import com.android.systemui.recents.RecentsActivity; +import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.RecentsConfiguration; /** Manages the scrims for the various system bars. */ public class SystemBarScrimViews { + Context mContext; RecentsConfiguration mConfig; View mStatusBarScrimView; @@ -34,10 +40,22 @@ public class SystemBarScrimViews { boolean mHasStatusBarScrim; boolean mShouldAnimateNavBarScrim; - public SystemBarScrimViews(Activity activity, RecentsConfiguration config) { - mConfig = config; + int mNavBarScrimEnterDuration; + + Interpolator mFastOutSlowInInterpolator; + Interpolator mQuintOutInterpolator; + + public SystemBarScrimViews(Activity activity) { + mContext = activity; + mConfig = RecentsConfiguration.getInstance(); mStatusBarScrimView = activity.findViewById(R.id.status_bar_scrim); mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim); + mNavBarScrimEnterDuration = activity.getResources().getInteger( + R.integer.recents_nav_bar_scrim_enter_duration); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(activity, + com.android.internal.R.interpolator.fast_out_slow_in); + mQuintOutInterpolator = AnimationUtils.loadInterpolator(activity, + com.android.internal.R.interpolator.decelerate_quint); } /** @@ -45,10 +63,11 @@ public class SystemBarScrimViews { * the first draw. */ public void prepareEnterRecentsAnimation() { - mHasNavBarScrim = mConfig.hasNavBarScrim(); - mShouldAnimateNavBarScrim = mConfig.shouldAnimateNavBarScrim(); - mHasStatusBarScrim = mConfig.hasStatusBarScrim(); - mShouldAnimateStatusBarScrim = mConfig.shouldAnimateStatusBarScrim(); + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); + mHasNavBarScrim = launchState.hasNavBarScrim(); + mShouldAnimateNavBarScrim = launchState.shouldAnimateNavBarScrim(); + mHasStatusBarScrim = launchState.hasStatusBarScrim(); + mShouldAnimateStatusBarScrim = launchState.shouldAnimateStatusBarScrim(); mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ? View.VISIBLE : View.INVISIBLE); @@ -60,15 +79,21 @@ public class SystemBarScrimViews { * Starts animating the scrim views when entering Recents. */ public void startEnterRecentsAnimation() { + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); + int transitionEnterFromAppDelay = mContext.getResources().getInteger( + R.integer.recents_enter_from_app_transition_duration); + int transitionEnterFromHomeDelay = mContext.getResources().getInteger( + R.integer.recents_enter_from_home_transition_duration); + if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) { mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight()); mStatusBarScrimView.animate() .translationY(0) - .setStartDelay(mConfig.launchedFromHome ? - mConfig.transitionEnterFromHomeDelay : - mConfig.transitionEnterFromAppDelay) - .setDuration(mConfig.navBarScrimEnterDuration) - .setInterpolator(mConfig.quintOutInterpolator) + .setStartDelay(launchState.launchedFromHome ? + transitionEnterFromHomeDelay : + transitionEnterFromAppDelay) + .setDuration(mNavBarScrimEnterDuration) + .setInterpolator(mQuintOutInterpolator) .withStartAction(new Runnable() { @Override public void run() { @@ -81,11 +106,11 @@ public class SystemBarScrimViews { mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight()); mNavBarScrimView.animate() .translationY(0) - .setStartDelay(mConfig.launchedFromHome ? - mConfig.transitionEnterFromHomeDelay : - mConfig.transitionEnterFromAppDelay) - .setDuration(mConfig.navBarScrimEnterDuration) - .setInterpolator(mConfig.quintOutInterpolator) + .setStartDelay(launchState.launchedFromHome ? + transitionEnterFromHomeDelay : + transitionEnterFromAppDelay) + .setDuration(mNavBarScrimEnterDuration) + .setInterpolator(mQuintOutInterpolator) .withStartAction(new Runnable() { @Override public void run() { @@ -101,20 +126,22 @@ public class SystemBarScrimViews { * going home). */ public void startExitRecentsAnimation() { + int taskViewExitToAppDuration = mContext.getResources().getInteger( + R.integer.recents_task_exit_to_app_duration); if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) { mStatusBarScrimView.animate() .translationY(-mStatusBarScrimView.getMeasuredHeight()) .setStartDelay(0) - .setDuration(mConfig.taskViewExitToAppDuration) - .setInterpolator(mConfig.fastOutSlowInInterpolator) + .setDuration(taskViewExitToAppDuration) + .setInterpolator(mFastOutSlowInInterpolator) .start(); } if (mHasNavBarScrim && mShouldAnimateNavBarScrim) { mNavBarScrimView.animate() .translationY(mNavBarScrimView.getMeasuredHeight()) .setStartDelay(0) - .setDuration(mConfig.taskViewExitToAppDuration) - .setInterpolator(mConfig.fastOutSlowInInterpolator) + .setDuration(taskViewExitToAppDuration) + .setInterpolator(mFastOutSlowInInterpolator) .start(); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index 4db8b3708bf3..b5ad112098e3 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -29,10 +29,10 @@ import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; - import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.recents.Constants; +import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.misc.DozeTrigger; import com.android.systemui.recents.misc.SystemServicesProxy; @@ -117,14 +117,17 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Set the stack first setStack(stack); mConfig = RecentsConfiguration.getInstance(); - mViewPool = new ViewPool<TaskView, Task>(context, this); + mViewPool = new ViewPool<>(context, this); mInflater = LayoutInflater.from(context); - mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(mConfig); - mFilterAlgorithm = new TaskStackViewFilterAlgorithm(mConfig, this, mViewPool); - mStackScroller = new TaskStackViewScroller(context, mConfig, mLayoutAlgorithm); + mLayoutAlgorithm = new TaskStackViewLayoutAlgorithm(context, mConfig); + mFilterAlgorithm = new TaskStackViewFilterAlgorithm(this, mViewPool); + mStackScroller = new TaskStackViewScroller(context, mLayoutAlgorithm); mStackScroller.setCallbacks(this); - mTouchHandler = new TaskStackViewTouchHandler(context, this, mConfig, mStackScroller); - mUIDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() { + mTouchHandler = new TaskStackViewTouchHandler(context, this, mStackScroller); + + int taskBarDismissDozeDelaySeconds = getResources().getInteger( + R.integer.recents_task_bar_dismiss_delay_seconds); + mUIDozeTrigger = new DozeTrigger(taskBarDismissDozeDelaySeconds, new Runnable() { @Override public void run() { // Show the task bar dismiss buttons @@ -731,8 +734,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int height = MeasureSpec.getSize(heightMeasureSpec); // Compute our stack/task rects - computeRects(width, height, mTaskStackBounds, mConfig.launchedWithAltTab, - mConfig.launchedFromHome); + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); + computeRects(width, height, mTaskStackBounds, launchState.launchedWithAltTab, + launchState.launchedFromHome); // If this is the first layout, then scroll to the front of the stack and synchronize the // stack views immediately to load all the views @@ -764,9 +768,11 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Measure the dismiss button if (mDismissAllButton != null) { int taskRectWidth = mLayoutAlgorithm.mTaskRect.width(); + int dismissAllButtonHeight = getResources().getDimensionPixelSize( + R.dimen.recents_dismiss_all_button_size); mDismissAllButton.measure( MeasureSpec.makeMeasureSpec(taskRectWidth, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(mConfig.dismissAllButtonSizePx, MeasureSpec.EXACTLY)); + MeasureSpec.makeMeasureSpec(dismissAllButtonHeight, MeasureSpec.EXACTLY)); } setMeasuredDimension(width, height); @@ -847,13 +853,14 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // When Alt-Tabbing, focus the previous task (but leave the animation until we finish the // enter animation). - if (mConfig.launchedWithAltTab) { - if (mConfig.launchedFromAppWithThumbnail) { + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); + if (launchState.launchedWithAltTab) { + if (launchState.launchedFromAppWithThumbnail) { focusTask(Math.max(0, mStack.getTaskCount() - 2), false, - mConfig.launchedHasConfigurationChanged); + launchState.launchedHasConfigurationChanged); } else { focusTask(Math.max(0, mStack.getTaskCount() - 1), false, - mConfig.launchedHasConfigurationChanged); + launchState.launchedHasConfigurationChanged); } } @@ -924,7 +931,9 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal // Start the focus animation when alt-tabbing ArrayList<Task> tasks = mStack.getTasks(); - if (mConfig.launchedWithAltTab && !mConfig.launchedHasConfigurationChanged && + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); + if (launchState.launchedWithAltTab && + !launchState.launchedHasConfigurationChanged && 0 <= mFocusedTaskIndex && mFocusedTaskIndex < tasks.size()) { TaskView tv = getChildViewForTask(tasks.get(mFocusedTaskIndex)); if (tv != null) { @@ -1115,7 +1124,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // Update the min/max scroll and animate other task views into their new positions - updateMinMaxScroll(true, mConfig.launchedWithAltTab, mConfig.launchedFromHome); + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); + updateMinMaxScroll(true, launchState.launchedWithAltTab, launchState.launchedFromHome); // Offset the stack by as much as the anchor task would otherwise move back if (pullStackForward) { @@ -1133,7 +1143,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal TaskView frontTv = getChildViewForTask(newFrontMostTask); if (frontTv != null) { frontTv.onTaskBound(newFrontMostTask); - frontTv.fadeInActionButton(0, mConfig.taskViewEnterFromAppDuration); + frontTv.fadeInActionButton(0, getResources().getInteger( + R.integer.recents_task_enter_from_app_duration)); } } @@ -1384,7 +1395,8 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal if (nextTv != null) { // Focus the next task, and only animate the visible state if we are launched // from Alt-Tab - nextTv.setFocusedTask(mConfig.launchedWithAltTab); + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); + nextTv.setFocusedTask(launchState.launchedWithAltTab); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java index 614ca53983ed..e9f6a46c9707 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewFilterAlgorithm.java @@ -17,8 +17,8 @@ package com.android.systemui.recents.views; import com.android.systemui.recents.Constants; -import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.model.Task; +import com.android.systemui.R; import java.util.ArrayList; import java.util.HashMap; @@ -27,13 +27,10 @@ import java.util.List; /* The layout logic for a TaskStackView */ public class TaskStackViewFilterAlgorithm { - RecentsConfiguration mConfig; TaskStackView mStackView; ViewPool<TaskView, Task> mViewPool; - public TaskStackViewFilterAlgorithm(RecentsConfiguration config, TaskStackView stackView, - ViewPool<TaskView, Task> viewPool) { - mConfig = config; + public TaskStackViewFilterAlgorithm(TaskStackView stackView, ViewPool<TaskView, Task> viewPool) { mStackView = stackView; mViewPool = viewPool; } @@ -126,7 +123,8 @@ public class TaskStackViewFilterAlgorithm { } } } - return mConfig.filteringNewViewsAnimDuration; + return mStackView.getResources().getInteger( + R.integer.recents_filter_animate_new_views_duration); } /** @@ -172,7 +170,8 @@ public class TaskStackViewFilterAlgorithm { childViewTransformsOut.put(tv, toTransform); offset++; } - return mConfig.filteringCurrentViewsAnimDuration; + return mStackView.getResources().getInteger( + R.integer.recents_filter_animate_current_views_duration); } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java index f6df881121d7..8128cac66c1c 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewLayoutAlgorithm.java @@ -16,11 +16,13 @@ package com.android.systemui.recents.views; +import android.content.Context; import android.graphics.Rect; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.misc.Utilities; import com.android.systemui.recents.model.Task; +import com.android.systemui.R; import java.util.ArrayList; import java.util.HashMap; @@ -48,6 +50,7 @@ public class TaskStackViewLayoutAlgorithm { } } + Context mContext; RecentsConfiguration mConfig; // The various rects that define the stack view @@ -71,7 +74,8 @@ public class TaskStackViewLayoutAlgorithm { static float[] xp; static float[] px; - public TaskStackViewLayoutAlgorithm(RecentsConfiguration config) { + public TaskStackViewLayoutAlgorithm(Context context, RecentsConfiguration config) { + mContext = context; mConfig = config; // Precompute the path @@ -87,7 +91,8 @@ public class TaskStackViewLayoutAlgorithm { mStackVisibleRect.bottom = mViewRect.bottom; int widthPadding = (int) (mConfig.taskStackWidthPaddingPct * mStackRect.width()); - int heightPadding = mConfig.taskStackTopPaddingPx; + int heightPadding = mContext.getResources().getDimensionPixelSize( + R.dimen.recents_stack_top_padding); mStackRect.inset(widthPadding, heightPadding); // Compute the task rect @@ -98,7 +103,8 @@ public class TaskStackViewLayoutAlgorithm { // Update the affiliation offsets float visibleTaskPct = 0.5f; - mWithinAffiliationOffset = mConfig.taskBarHeight; + mWithinAffiliationOffset = mContext.getResources().getDimensionPixelSize( + R.dimen.recents_task_bar_height); mBetweenAffiliationOffset = (int) (visibleTaskPct * mTaskRect.height()); } @@ -134,8 +140,10 @@ public class TaskStackViewLayoutAlgorithm { mStackRect.bottom)); float pDismissAllButtonOffset = 0f; if (Constants.DebugFlags.App.EnableDismissAll) { + int dismissAllButtonHeight = mContext.getResources().getDimensionPixelSize( + R.dimen.recents_dismiss_all_button_size); pDismissAllButtonOffset = pAtBottomOfStackRect - - screenYToCurveProgress(mStackVisibleRect.bottom - mConfig.dismissAllButtonSizePx); + screenYToCurveProgress(mStackVisibleRect.bottom - dismissAllButtonHeight); } // Update the task offsets @@ -177,6 +185,8 @@ public class TaskStackViewLayoutAlgorithm { // Walk backwards in the task stack and count the number of tasks and visible thumbnails int taskHeight = mTaskRect.height(); + int taskBarHeight = mContext.getResources().getDimensionPixelSize( + R.dimen.recents_task_bar_height); int numVisibleTasks = 1; int numVisibleThumbnails = 1; float progress = mTaskProgressMap.get(tasks.get(tasks.size() - 1).key) - mInitialScrollP; @@ -192,7 +202,7 @@ public class TaskStackViewLayoutAlgorithm { float scaleAtP = curveProgressToScale(progress); int scaleYOffsetAtP = (int) (((1f - scaleAtP) * taskHeight) / 2); int screenY = curveProgressToScreenY(progress) + scaleYOffsetAtP; - boolean hasVisibleThumbnail = (prevScreenY - screenY) > mConfig.taskBarHeight; + boolean hasVisibleThumbnail = (prevScreenY - screenY) > taskBarHeight; if (hasVisibleThumbnail) { numVisibleThumbnails++; numVisibleTasks++; @@ -251,8 +261,8 @@ public class TaskStackViewLayoutAlgorithm { } float scale = curveProgressToScale(pBounded); int scaleYOffset = (int) (((1f - scale) * mTaskRect.height()) / 2); - int minZ = mConfig.taskViewTranslationZMinPx; - int maxZ = mConfig.taskViewTranslationZMaxPx; + int minZ = mContext.getResources().getDimensionPixelSize(R.dimen.recents_task_view_z_min); + int maxZ = mContext.getResources().getDimensionPixelSize(R.dimen.recents_task_view_z_max); transformOut.scale = scale; transformOut.translationY = curveProgressToScreenY(pBounded) - mStackVisibleRect.top - scaleYOffset; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java index fabc86d7e53a..f0ae87f6f9ae 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewScroller.java @@ -21,9 +21,11 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.OverScroller; -import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.misc.Utilities; +import com.android.systemui.R; /* The scrolling logic for a TaskStackView */ public class TaskStackViewScroller { @@ -31,7 +33,7 @@ public class TaskStackViewScroller { public void onScrollChanged(float p); } - RecentsConfiguration mConfig; + Context mContext; TaskStackViewLayoutAlgorithm mLayoutAlgorithm; TaskStackViewScrollerCallbacks mCb; @@ -41,10 +43,14 @@ public class TaskStackViewScroller { ObjectAnimator mScrollAnimator; float mFinalAnimatedScroll; - public TaskStackViewScroller(Context context, RecentsConfiguration config, TaskStackViewLayoutAlgorithm layoutAlgorithm) { - mConfig = config; + Interpolator mLinearOutSlowInInterpolator; + + public TaskStackViewScroller(Context context, TaskStackViewLayoutAlgorithm layoutAlgorithm) { + mContext = context; mScroller = new OverScroller(context); mLayoutAlgorithm = layoutAlgorithm; + mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.linear_out_slow_in); setStackScroll(getStackScroll()); } @@ -140,8 +146,9 @@ public class TaskStackViewScroller { mFinalAnimatedScroll = newScroll; mScrollAnimator = ObjectAnimator.ofFloat(this, "stackScroll", curScroll, newScroll); - mScrollAnimator.setDuration(mConfig.taskStackScrollDuration); - mScrollAnimator.setInterpolator(mConfig.linearOutSlowInInterpolator); + mScrollAnimator.setDuration(mContext.getResources().getInteger( + R.integer.recents_animate_task_stack_scroll_duration)); + mScrollAnimator.setInterpolator(mLinearOutSlowInInterpolator); mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 7d079d90fee5..86eced8a763d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -26,7 +26,7 @@ import android.view.ViewParent; import com.android.internal.logging.MetricsLogger; import com.android.systemui.recents.Constants; import com.android.systemui.recents.Recents; -import com.android.systemui.recents.RecentsConfiguration; +import com.android.systemui.R; import java.util.List; @@ -34,7 +34,7 @@ import java.util.List; class TaskStackViewTouchHandler implements SwipeHelper.Callback { static int INACTIVE_POINTER_ID = -1; - RecentsConfiguration mConfig; + Context mContext; TaskStackView mSv; TaskStackViewScroller mScroller; VelocityTracker mVelocityTracker; @@ -62,7 +62,8 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { boolean mInterceptedBySwipeHelper; public TaskStackViewTouchHandler(Context context, TaskStackView sv, - RecentsConfiguration config, TaskStackViewScroller scroller) { + TaskStackViewScroller scroller) { + mContext = context; ViewConfiguration configuration = ViewConfiguration.get(context); mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); @@ -71,10 +72,9 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { mWindowTouchSlop = configuration.getScaledWindowTouchSlop(); mSv = sv; mScroller = scroller; - mConfig = config; float densityScale = context.getResources().getDisplayMetrics().density; - mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop); + mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, densityScale, mPagingTouchSlop); mSwipeHelper.setMinAlpha(1f); } @@ -268,7 +268,8 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { if (Float.compare(overScrollAmount, 0f) != 0) { // Bound the overscroll to a fixed amount, and inversely scale the y-movement // relative to how close we are to the max overscroll - float maxOverScroll = mConfig.taskStackOverscrollPct; + float maxOverScroll = mContext.getResources().getFloat( + R.dimen.recents_stack_overscroll_percentage); deltaP *= (1f - (Math.min(maxOverScroll, overScrollAmount) / maxOverScroll)); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index 373fe7b60fd6..bbbaccf3fa5f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -20,16 +20,25 @@ import android.animation.Animator; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; -import android.graphics.*; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.Outline; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; import android.util.AttributeSet; -import android.view.accessibility.AccessibilityManager; import android.view.View; import android.view.ViewOutlineProvider; +import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.FrameLayout; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.recents.Constants; +import com.android.systemui.recents.RecentsActivityLaunchState; import com.android.systemui.recents.RecentsConfiguration; import com.android.systemui.recents.misc.Utilities; import com.android.systemui.recents.model.Task; @@ -75,6 +84,10 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, View mActionButtonView; TaskViewCallbacks mCb; + Interpolator mFastOutSlowInInterpolator; + Interpolator mFastOutLinearInInterpolator; + Interpolator mQuintOutInterpolator; + // Optimizations ValueAnimator.AnimatorUpdateListener mUpdateDimListener = new ValueAnimator.AnimatorUpdateListener() { @@ -99,14 +112,22 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, public TaskView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); + Resources res = context.getResources(); mConfig = RecentsConfiguration.getInstance(); - mMaxDimScale = mConfig.taskStackMaxDim / 255f; + mMaxDimScale = res.getInteger(R.integer.recents_max_task_stack_view_dim) / 255f; mClipViewInStack = true; - mViewBounds = new AnimateableViewBounds(this, mConfig.taskViewRoundedCornerRadiusPx); + mViewBounds = new AnimateableViewBounds(this, res.getDimensionPixelSize( + R.dimen.recents_task_view_rounded_corners_radius)); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); + mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_linear_in); + mQuintOutInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.decelerate_quint); setTaskProgress(getTaskProgress()); setDim(getDim()); if (mConfig.fakeShadows) { - setBackground(new FakeShadowDrawable(context.getResources(), mConfig)); + setBackground(new FakeShadowDrawable(res, mConfig)); } setOutlineProvider(mViewBounds); } @@ -159,6 +180,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, int widthWithoutPadding = width - mPaddingLeft - mPaddingRight; int heightWithoutPadding = height - mPaddingTop - mPaddingBottom; + int taskBarHeight = getResources().getDimensionPixelSize(R.dimen.recents_task_bar_height); // Measure the content mContent.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY), @@ -166,7 +188,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, // Measure the bar view, and action button mHeaderView.measure(MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(mConfig.taskBarHeight, MeasureSpec.EXACTLY)); + MeasureSpec.makeMeasureSpec(taskBarHeight, MeasureSpec.EXACTLY)); mActionButtonView.measure( MeasureSpec.makeMeasureSpec(widthWithoutPadding, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(heightWithoutPadding, MeasureSpec.AT_MOST)); @@ -186,7 +208,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, void updateViewPropertiesToTaskTransform(TaskViewTransform toTransform, int duration, ValueAnimator.AnimatorUpdateListener updateCallback) { // Apply the transform - toTransform.applyToTaskView(this, duration, mConfig.fastOutSlowInInterpolator, false, + toTransform.applyToTaskView(this, duration, mFastOutSlowInInterpolator, false, !mConfig.fakeShadows, updateCallback); // Update the task progress @@ -238,10 +260,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, * first layout because the actual animation into recents may take a long time. */ void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, boolean occludesLaunchTarget, int offscreenY) { + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); int initialDim = getDim(); - if (mConfig.launchedHasConfigurationChanged) { + if (launchState.launchedHasConfigurationChanged) { // Just load the views as-is - } else if (mConfig.launchedFromAppWithThumbnail) { + } else if (launchState.launchedFromAppWithThumbnail) { if (isTaskViewLaunchTargetTask) { // Set the dim to 0 so we can animate it in initialDim = 0; @@ -252,7 +275,7 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, setTranslationY(offscreenY); } - } else if (mConfig.launchedFromHome) { + } else if (launchState.launchedFromHome) { // Move the task view off screen (below) so we can animate it in setTranslationY(offscreenY); setTranslationZ(0); @@ -267,45 +290,59 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, /** Animates this task view as it enters recents */ void startEnterRecentsAnimation(final ViewAnimation.TaskViewEnterContext ctx) { + RecentsActivityLaunchState launchState = mConfig.getLaunchState(); + Resources res = mContext.getResources(); final TaskViewTransform transform = ctx.currentTaskTransform; + final int transitionEnterFromAppDelay = res.getInteger( + R.integer.recents_enter_from_app_transition_duration); + final int transitionEnterFromHomeDelay = res.getInteger( + R.integer.recents_enter_from_home_transition_duration); + final int taskViewEnterFromAppDuration = res.getInteger( + R.integer.recents_task_enter_from_app_duration); + final int taskViewEnterFromHomeDuration = res.getInteger( + R.integer.recents_task_enter_from_home_duration); + final int taskViewEnterFromHomeStaggerDelay = res.getInteger( + R.integer.recents_task_enter_from_home_stagger_delay); + final int taskViewAffiliateGroupEnterOffset = res.getDimensionPixelSize( + R.dimen.recents_task_view_affiliate_group_enter_offset); int startDelay = 0; - if (mConfig.launchedFromAppWithThumbnail) { + if (launchState.launchedFromAppWithThumbnail) { if (mTask.isLaunchTarget) { // Animate the dim/overlay if (Constants.DebugFlags.App.EnableThumbnailAlphaOnFrontmost) { // Animate the thumbnail alpha before the dim animation (to prevent updating the // hardware layer) - mThumbnailView.startEnterRecentsAnimation(mConfig.transitionEnterFromAppDelay, + mThumbnailView.startEnterRecentsAnimation(transitionEnterFromAppDelay, new Runnable() { @Override public void run() { - animateDimToProgress(0, mConfig.taskViewEnterFromAppDuration, + animateDimToProgress(0, taskViewEnterFromAppDuration, ctx.postAnimationTrigger.decrementOnAnimationEnd()); } }); } else { // Immediately start the dim animation - animateDimToProgress(mConfig.transitionEnterFromAppDelay, - mConfig.taskViewEnterFromAppDuration, + animateDimToProgress(transitionEnterFromAppDelay, + taskViewEnterFromAppDuration, ctx.postAnimationTrigger.decrementOnAnimationEnd()); } ctx.postAnimationTrigger.increment(); // Animate the action button in - fadeInActionButton(mConfig.transitionEnterFromAppDelay, - mConfig.taskViewEnterFromAppDuration); + fadeInActionButton(transitionEnterFromAppDelay, + taskViewEnterFromAppDuration); } else { // Animate the task up if it was occluding the launch target if (ctx.currentTaskOccludesLaunchTarget) { - setTranslationY(transform.translationY + mConfig.taskViewAffiliateGroupEnterOffsetPx); + setTranslationY(transform.translationY + taskViewAffiliateGroupEnterOffset); setAlpha(0f); animate().alpha(1f) .translationY(transform.translationY) - .setStartDelay(mConfig.transitionEnterFromAppDelay) + .setStartDelay(transitionEnterFromAppDelay) .setUpdateListener(null) - .setInterpolator(mConfig.fastOutSlowInInterpolator) - .setDuration(mConfig.taskViewEnterFromHomeDuration) + .setInterpolator(mFastOutSlowInInterpolator) + .setDuration(taskViewEnterFromHomeDuration) .withEndAction(new Runnable() { @Override public void run() { @@ -317,13 +354,13 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, ctx.postAnimationTrigger.increment(); } } - startDelay = mConfig.transitionEnterFromAppDelay; + startDelay = transitionEnterFromAppDelay; - } else if (mConfig.launchedFromHome) { + } else if (launchState.launchedFromHome) { // Animate the tasks up int frontIndex = (ctx.currentStackViewCount - ctx.currentStackViewIndex - 1); - int delay = mConfig.transitionEnterFromHomeDelay + - frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay; + int delay = transitionEnterFromHomeDelay + + frontIndex * taskViewEnterFromHomeStaggerDelay; setScaleX(transform.scale); setScaleY(transform.scale); @@ -334,9 +371,9 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, .translationY(transform.translationY) .setStartDelay(delay) .setUpdateListener(ctx.updateListener) - .setInterpolator(mConfig.quintOutInterpolator) - .setDuration(mConfig.taskViewEnterFromHomeDuration + - frontIndex * mConfig.taskViewEnterFromHomeStaggerDelay) + .setInterpolator(mQuintOutInterpolator) + .setDuration(taskViewEnterFromHomeDuration + + frontIndex * taskViewEnterFromHomeStaggerDelay) .withEndAction(new Runnable() { @Override public void run() { @@ -373,12 +410,14 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, /** Animates this task view as it leaves recents by pressing home. */ void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) { + int taskViewExitToHomeDuration = getResources().getInteger( + R.integer.recents_task_exit_to_home_duration); animate() .translationY(ctx.offscreenTranslationY) .setStartDelay(0) .setUpdateListener(null) - .setInterpolator(mConfig.fastOutLinearInInterpolator) - .setDuration(mConfig.taskViewExitToHomeDuration) + .setInterpolator(mFastOutLinearInInterpolator) + .setDuration(taskViewExitToHomeDuration) .withEndAction(ctx.postAnimationTrigger.decrementAsRunnable()) .start(); ctx.postAnimationTrigger.increment(); @@ -392,6 +431,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, /** Animates this task view as it exits recents */ void startLaunchTaskAnimation(final Runnable postAnimRunnable, boolean isLaunchingTask, boolean occludesLaunchTarget, boolean lockToTask) { + final int taskViewExitToAppDuration = mContext.getResources().getInteger( + R.integer.recents_task_exit_to_app_duration); + final int taskViewAffiliateGroupEnterOffset = mContext.getResources().getDimensionPixelSize( + R.dimen.recents_task_view_affiliate_group_enter_offset); + if (isLaunchingTask) { // Animate the thumbnail alpha back into full opacity for the window animation out mThumbnailView.startLaunchTaskAnimation(postAnimRunnable); @@ -399,8 +443,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, // Animate the dim if (mDimAlpha > 0) { ObjectAnimator anim = ObjectAnimator.ofInt(this, "dim", 0); - anim.setDuration(mConfig.taskViewExitToAppDuration); - anim.setInterpolator(mConfig.fastOutLinearInInterpolator); + anim.setDuration(taskViewExitToAppDuration); + anim.setInterpolator(mFastOutLinearInInterpolator); anim.start(); } @@ -414,8 +458,8 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, mActionButtonView.animate() .alpha(0f) .setStartDelay(0) - .setDuration(mConfig.taskViewExitToAppDuration) - .setInterpolator(mConfig.fastOutLinearInInterpolator) + .setDuration(taskViewExitToAppDuration) + .setInterpolator(mFastOutLinearInInterpolator) .start(); } else { // Hide the dismiss button @@ -424,11 +468,11 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, // animate it away first if (occludesLaunchTarget) { animate().alpha(0f) - .translationY(getTranslationY() + mConfig.taskViewAffiliateGroupEnterOffsetPx) + .translationY(getTranslationY() + taskViewAffiliateGroupEnterOffset) .setStartDelay(0) .setUpdateListener(null) - .setInterpolator(mConfig.fastOutLinearInInterpolator) - .setDuration(mConfig.taskViewExitToAppDuration) + .setInterpolator(mFastOutLinearInInterpolator) + .setDuration(taskViewExitToAppDuration) .start(); } } @@ -436,15 +480,20 @@ public class TaskView extends FrameLayout implements Task.TaskCallbacks, /** Animates the deletion of this task view */ void startDeleteTaskAnimation(final Runnable r, int delay) { + int taskViewRemoveAnimDuration = getResources().getInteger( + R.integer.recents_animate_task_view_remove_duration); + int taskViewRemoveAnimTranslationXPx = getResources().getDimensionPixelSize( + R.dimen.recents_task_view_remove_anim_translation_x); + // Disabling clipping with the stack while the view is animating away setClipViewInStack(false); - animate().translationX(mConfig.taskViewRemoveAnimTranslationXPx) + animate().translationX(taskViewRemoveAnimTranslationXPx) .alpha(0f) .setStartDelay(delay) .setUpdateListener(null) - .setInterpolator(mConfig.fastOutSlowInInterpolator) - .setDuration(mConfig.taskViewRemoveAnimDuration) + .setInterpolator(mFastOutSlowInInterpolator) + .setDuration(taskViewRemoveAnimDuration) .withEndAction(new Runnable() { @Override public void run() { diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index 3e9410e1348a..f68dd64b0bf2 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -26,19 +26,21 @@ import android.content.Context; import android.content.res.ColorStateList; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.drawable.ColorDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.RippleDrawable; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; +import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.view.View; import android.view.ViewOutlineProvider; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; @@ -67,6 +69,8 @@ public class TaskViewHeader extends FrameLayout { boolean mCurrentPrimaryColorIsDark; int mCurrentPrimaryColor; int mBackgroundColor; + int mCornerRadius; + int mHighlightHeight; Drawable mLightDismissDrawable; Drawable mDarkDismissDrawable; RippleDrawable mBackground; @@ -81,6 +85,9 @@ public class TaskViewHeader extends FrameLayout { Paint mDimLayerPaint = new Paint(); PorterDuffColorFilter mDimColorFilter = new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_ATOP); + Interpolator mFastOutSlowInInterpolator; + Interpolator mFastOutLinearInInterpolator; + boolean mLayersDisabled; public TaskViewHeader(Context context) { @@ -113,13 +120,21 @@ public class TaskViewHeader extends FrameLayout { mDarkDismissDrawable = context.getDrawable(R.drawable.recents_dismiss_dark); mDismissContentDescription = context.getString(R.string.accessibility_recents_item_will_be_dismissed); + mCornerRadius = getResources().getDimensionPixelSize( + R.dimen.recents_task_view_rounded_corners_radius); + mHighlightHeight = getResources().getDimensionPixelSize( + R.dimen.recents_task_view_highlight); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); + mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_linear_in); // Configure the highlight paint if (sHighlightPaint == null) { sHighlightPaint = new Paint(); sHighlightPaint.setStyle(Paint.Style.STROKE); - sHighlightPaint.setStrokeWidth(mConfig.taskViewHighlightPx); - sHighlightPaint.setColor(mConfig.taskBarViewHighlightColor); + sHighlightPaint.setStrokeWidth(mHighlightHeight); + sHighlightPaint.setColor(context.getColor(R.color.recents_task_bar_highlight_color)); sHighlightPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD)); sHighlightPaint.setAntiAlias(true); } @@ -154,8 +169,8 @@ public class TaskViewHeader extends FrameLayout { @Override protected void onDraw(Canvas canvas) { // Draw the highlight at the top edge (but put the bottom edge just out of view) - float offset = (float) Math.ceil(mConfig.taskViewHighlightPx / 2f); - float radius = mConfig.taskViewRoundedCornerRadiusPx; + float offset = (float) Math.ceil(mHighlightHeight / 2f); + float radius = mCornerRadius; int count = canvas.save(Canvas.CLIP_SAVE_FLAG); canvas.clipRect(0, 0, getMeasuredWidth(), getMeasuredHeight()); canvas.drawRoundRect(-offset, 0f, (float) getMeasuredWidth() + offset, @@ -207,10 +222,15 @@ public class TaskViewHeader extends FrameLayout { mBackgroundColorDrawable.setColor(t.colorPrimary); mBackgroundColor = t.colorPrimary; } + + int taskBarViewLightTextColor = getResources().getColor( + R.color.recents_task_bar_light_text_color); + int taskBarViewDarkTextColor = getResources().getColor( + R.color.recents_task_bar_dark_text_color); mCurrentPrimaryColor = t.colorPrimary; mCurrentPrimaryColorIsDark = t.useLightOnPrimaryColor; mActivityDescription.setTextColor(t.useLightOnPrimaryColor ? - mConfig.taskBarViewLightTextColor : mConfig.taskBarViewDarkTextColor); + taskBarViewLightTextColor : taskBarViewDarkTextColor); mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ? mLightDismissDrawable : mDarkDismissDrawable); mDismissButton.setContentDescription(String.format(mDismissContentDescription, @@ -262,12 +282,14 @@ public class TaskViewHeader extends FrameLayout { /** Animates this task bar dismiss button when launching a task. */ void startLaunchTaskDismissAnimation() { if (mDismissButton.getVisibility() == View.VISIBLE) { + int taskViewExitToAppDuration = mContext.getResources().getInteger( + R.integer.recents_task_exit_to_app_duration); mDismissButton.animate().cancel(); mDismissButton.animate() .alpha(0f) .setStartDelay(0) - .setInterpolator(mConfig.fastOutSlowInInterpolator) - .setDuration(mConfig.taskViewExitToAppDuration) + .setInterpolator(mFastOutSlowInInterpolator) + .setDuration(taskViewExitToAppDuration) .start(); } } @@ -280,8 +302,9 @@ public class TaskViewHeader extends FrameLayout { mDismissButton.animate() .alpha(1f) .setStartDelay(0) - .setInterpolator(mConfig.fastOutLinearInInterpolator) - .setDuration(mConfig.taskViewEnterFromAppDuration) + .setInterpolator(mFastOutLinearInInterpolator) + .setDuration(getResources().getInteger( + R.integer.recents_task_enter_from_app_duration)) .start(); } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java index 117a7d328692..6c83beeff1da 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java @@ -32,9 +32,11 @@ import android.graphics.RectF; import android.graphics.Shader; import android.util.AttributeSet; import android.view.View; -import com.android.systemui.recents.RecentsConfiguration; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import com.android.systemui.recents.misc.Utilities; import com.android.systemui.recents.model.Task; +import com.android.systemui.R; /** @@ -43,9 +45,8 @@ import com.android.systemui.recents.model.Task; */ public class TaskViewThumbnail extends View { - RecentsConfiguration mConfig; - // Drawing + int mCornerRadius; float mDimAlpha; Matrix mScaleMatrix = new Matrix(); Paint mDrawPaint = new Paint(); @@ -54,6 +55,8 @@ public class TaskViewThumbnail extends View { BitmapShader mBitmapShader; LightingColorFilter mLightingColorFilter = new LightingColorFilter(0xffffffff, 0); + Interpolator mFastOutSlowInInterpolator; + // Thumbnail alpha float mThumbnailAlpha; ValueAnimator mThumbnailAlphaAnimator; @@ -89,15 +92,18 @@ public class TaskViewThumbnail extends View { public TaskViewThumbnail(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mConfig = RecentsConfiguration.getInstance(); mDrawPaint.setColorFilter(mLightingColorFilter); mDrawPaint.setFilterBitmap(true); mDrawPaint.setAntiAlias(true); + mCornerRadius = getResources().getDimensionPixelSize( + R.dimen.recents_task_view_rounded_corners_radius); + mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context, + com.android.internal.R.interpolator.fast_out_slow_in); } @Override protected void onFinishInflate() { - mThumbnailAlpha = mConfig.taskViewThumbnailAlpha; + mThumbnailAlpha = getResources().getFloat(R.dimen.recents_task_view_thumbnail_alpha); updateThumbnailPaintFilter(); } @@ -117,8 +123,8 @@ public class TaskViewThumbnail extends View { } // Draw the thumbnail with the rounded corners canvas.drawRoundRect(0, 0, getWidth(), getHeight(), - mConfig.taskViewRoundedCornerRadiusPx, - mConfig.taskViewRoundedCornerRadiusPx, mDrawPaint); + mCornerRadius, + mCornerRadius, mDrawPaint); } /** Sets the thumbnail to a given bitmap. */ @@ -215,8 +221,10 @@ public class TaskViewThumbnail extends View { startFadeAnimation(1f, 0, 150, null); } } else { - if (Float.compare(getAlpha(), mConfig.taskViewThumbnailAlpha) != 0) { - startFadeAnimation(mConfig.taskViewThumbnailAlpha, 0, 150, null); + float taskViewThumbnailAlpha = getResources().getFloat( + R.dimen.recents_task_view_thumbnail_alpha); + if (Float.compare(getAlpha(), taskViewThumbnailAlpha) != 0) { + startFadeAnimation(taskViewThumbnailAlpha, 0, 150, null); } } } @@ -229,20 +237,26 @@ public class TaskViewThumbnail extends View { if (isTaskViewLaunchTargetTask) { mThumbnailAlpha = 1f; } else { - mThumbnailAlpha = mConfig.taskViewThumbnailAlpha; + mThumbnailAlpha = getResources().getFloat( + R.dimen.recents_task_view_thumbnail_alpha); } updateThumbnailPaintFilter(); } /** Animates this task thumbnail as it enters Recents. */ void startEnterRecentsAnimation(int delay, Runnable postAnimRunnable) { - startFadeAnimation(mConfig.taskViewThumbnailAlpha, delay, - mConfig.taskViewEnterFromAppDuration, postAnimRunnable); + float taskViewThumbnailAlpha = getResources().getFloat( + R.dimen.recents_task_view_thumbnail_alpha); + startFadeAnimation(taskViewThumbnailAlpha, delay, + getResources().getInteger(R.integer.recents_task_enter_from_app_duration), + postAnimRunnable); } /** Animates this task thumbnail as it exits Recents. */ void startLaunchTaskAnimation(Runnable postAnimRunnable) { - startFadeAnimation(1f, 0, mConfig.taskViewExitToAppDuration, postAnimRunnable); + int taskViewExitToAppDuration = mContext.getResources().getInteger( + R.integer.recents_task_exit_to_app_duration); + startFadeAnimation(1f, 0, taskViewExitToAppDuration, postAnimRunnable); } /** Starts a new thumbnail alpha animation. */ @@ -251,7 +265,7 @@ public class TaskViewThumbnail extends View { mThumbnailAlphaAnimator = ValueAnimator.ofFloat(mThumbnailAlpha, finalAlpha); mThumbnailAlphaAnimator.setStartDelay(delay); mThumbnailAlphaAnimator.setDuration(duration); - mThumbnailAlphaAnimator.setInterpolator(mConfig.fastOutSlowInInterpolator); + mThumbnailAlphaAnimator.setInterpolator(mFastOutSlowInInterpolator); mThumbnailAlphaAnimator.addUpdateListener(mThumbnailAlphaUpdateListener); if (postAnimRunnable != null) { mThumbnailAlphaAnimator.addListener(new AnimatorListenerAdapter() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java index 0f9dd5cfc70b..e0823b41554d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java @@ -182,10 +182,10 @@ public class SecurityControllerImpl implements SecurityController { @Override public void onUserSwitched(int newUserId) { mCurrentUserId = newUserId; - if (mUserManager.getUserInfo(newUserId).isRestricted()) { + final UserInfo newUserInfo = mUserManager.getUserInfo(newUserId); + if (newUserInfo.isRestricted()) { // VPN for a restricted profile is routed through its owner user - // TODO: http://b/22950929 - mVpnUserId = UserHandle.USER_SYSTEM; + mVpnUserId = newUserInfo.restrictedProfileParentId; } else { mVpnUserId = mCurrentUserId; } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 6190a5ab357e..a7e6f1187719 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -754,6 +754,8 @@ public class ConnectivityService extends IConnectivityManager.Stub IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_USER_STARTING); intentFilter.addAction(Intent.ACTION_USER_STOPPING); + intentFilter.addAction(Intent.ACTION_USER_ADDED); + intentFilter.addAction(Intent.ACTION_USER_REMOVED); mContext.registerReceiverAsUser( mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); @@ -3525,6 +3527,26 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void onUserAdded(int userId) { + synchronized(mVpns) { + final int vpnsSize = mVpns.size(); + for (int i = 0; i < vpnsSize; i++) { + Vpn vpn = mVpns.valueAt(i); + vpn.onUserAdded(userId); + } + } + } + + private void onUserRemoved(int userId) { + synchronized(mVpns) { + final int vpnsSize = mVpns.size(); + for (int i = 0; i < vpnsSize; i++) { + Vpn vpn = mVpns.valueAt(i); + vpn.onUserRemoved(userId); + } + } + } + private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -3536,6 +3558,10 @@ public class ConnectivityService extends IConnectivityManager.Stub onUserStart(userId); } else if (Intent.ACTION_USER_STOPPING.equals(action)) { onUserStop(userId); + } else if (Intent.ACTION_USER_ADDED.equals(action)) { + onUserAdded(userId); + } else if (Intent.ACTION_USER_REMOVED.equals(action)) { + onUserRemoved(userId); } } }; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index ed92579dc21e..6b5f2056ec82 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -3110,8 +3110,9 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityRecord r = task.topRunningActivityLocked(null); if (r != null) { final ActivityStack stack = task.stack; - final boolean resizedByUser = resizeMode == RESIZE_MODE_USER; - final boolean preserveWindow = resizedByUser && !changedStacks; + final boolean preserveWindow = !changedStacks && + (resizeMode == RESIZE_MODE_USER + || resizeMode == RESIZE_MODE_SYSTEM_SCREEN_ROTATION); kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow); // All other activities must be made visible with their correct configuration. ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS); @@ -4626,16 +4627,6 @@ public final class ActivityStackSupervisor implements DisplayListener { return mActivityDisplay != null; } - void getBounds(Point outBounds) { - synchronized (mService) { - if (mActivityDisplay != null) { - mActivityDisplay.getBounds(outBounds); - } else { - outBounds.set(0, 0); - } - } - } - // TODO: Make sure every change to ActivityRecord.visible results in a call to this. void setVisible(boolean visible) { if (mVisible != visible) { @@ -4807,12 +4798,6 @@ public final class ActivityStackSupervisor implements DisplayListener { mStacks.remove(stack); } - void getBounds(Point bounds) { - mDisplay.getDisplayInfo(mDisplayInfo); - bounds.x = mDisplayInfo.appWidth; - bounds.y = mDisplayInfo.appHeight; - } - void setVisibleBehindActivity(ActivityRecord r) { mVisibleBehindActivity = r; } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 3df3cd0b587b..2bea278aad1a 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -20,9 +20,6 @@ import static android.Manifest.permission.BIND_VPN_SERVICE; import static android.net.ConnectivityManager.NETID_UNSET; import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNREACHABLE; -import static android.os.UserHandle.PER_USER_RANGE; -import static android.system.OsConstants.AF_INET; -import static android.system.OsConstants.AF_INET6; import android.Manifest; import android.app.AppGlobals; @@ -34,13 +31,11 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; import android.net.ConnectivityManager; -import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; import android.net.IpPrefix; import android.net.LinkAddress; @@ -126,7 +121,6 @@ public class Vpn { /* list of users using this VPN. */ @GuardedBy("this") private List<UidRange> mVpnUsers = null; - private BroadcastReceiver mUserIntentReceiver = null; // Handle of user initiating VPN. private final int mUserHandle; @@ -146,31 +140,6 @@ public class Vpn { } catch (RemoteException e) { Log.wtf(TAG, "Problem registering observer", e); } - // TODO: http://b/22950929 - if (userHandle == UserHandle.USER_SYSTEM) { - // Owner's VPN also needs to handle restricted users - mUserIntentReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, - UserHandle.USER_NULL); - if (userHandle == UserHandle.USER_NULL) return; - - if (Intent.ACTION_USER_ADDED.equals(action)) { - onUserAdded(userHandle); - } else if (Intent.ACTION_USER_REMOVED.equals(action)) { - onUserRemoved(userHandle); - } - } - }; - - IntentFilter intentFilter = new IntentFilter(); - intentFilter.addAction(Intent.ACTION_USER_ADDED); - intentFilter.addAction(Intent.ACTION_USER_REMOVED); - mContext.registerReceiverAsUser( - mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); - } mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, ""); // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332 @@ -439,8 +408,8 @@ public class Vpn { } addVpnUserLocked(mUserHandle); - // If we are owner assign all Restricted Users to this VPN - if (mUserHandle == UserHandle.USER_OWNER) { + // If the user can have restricted profiles, assign all its restricted profiles to this VPN + if (canHaveRestrictedProfile(mUserHandle)) { token = Binder.clearCallingIdentity(); List<UserInfo> users; try { @@ -449,7 +418,7 @@ public class Vpn { Binder.restoreCallingIdentity(token); } for (UserInfo user : users) { - if (user.isRestricted()) { + if (user.isRestricted() && (user.restrictedProfileParentId == mUserHandle)) { addVpnUserLocked(user.id); } } @@ -457,6 +426,15 @@ public class Vpn { mNetworkAgent.addUidRanges(mVpnUsers.toArray(new UidRange[mVpnUsers.size()])); } + private boolean canHaveRestrictedProfile(int userId) { + long token = Binder.clearCallingIdentity(); + try { + return UserManager.get(mContext).canHaveRestrictedProfile(userId); + } finally { + Binder.restoreCallingIdentity(token); + } + } + private void agentDisconnect(NetworkInfo networkInfo, NetworkAgent networkAgent) { networkInfo.setIsAvailable(false); networkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null); @@ -681,12 +659,11 @@ public class Vpn { mStatusIntent = null; } - private void onUserAdded(int userHandle) { - // If the user is restricted tie them to the owner's VPN - synchronized(Vpn.this) { - UserManager mgr = UserManager.get(mContext); - UserInfo user = mgr.getUserInfo(userHandle); - if (user.isRestricted()) { + public void onUserAdded(int userHandle) { + // If the user is restricted tie them to the parent user's VPN + UserInfo user = UserManager.get(mContext).getUserInfo(userHandle); + if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) { + synchronized(Vpn.this) { try { addVpnUserLocked(userHandle); if (mNetworkAgent != null) { @@ -700,12 +677,11 @@ public class Vpn { } } - private void onUserRemoved(int userHandle) { + public void onUserRemoved(int userHandle) { // clean up if restricted - synchronized(Vpn.this) { - UserManager mgr = UserManager.get(mContext); - UserInfo user = mgr.getUserInfo(userHandle); - if (user.isRestricted()) { + UserInfo user = UserManager.get(mContext).getUserInfo(userHandle); + if (user.isRestricted() && user.restrictedProfileParentId == mUserHandle) { + synchronized(Vpn.this) { try { removeVpnUserLocked(userHandle); } catch (Exception e) { diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 0577d5905085..de106a1d0d3c 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -131,7 +131,7 @@ public class UserManagerService extends IUserManager.Stub { private static final int MIN_USER_ID = 10; - private static final int USER_VERSION = 5; + private static final int USER_VERSION = 6; private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms @@ -408,6 +408,24 @@ public class UserManagerService extends IUserManager.Stub { } } + @Override + public boolean canHaveRestrictedProfile(int userId) { + checkManageUsersPermission("canHaveRestrictedProfile"); + synchronized (mPackagesLock) { + final UserInfo userInfo = getUserInfoLocked(userId); + if (userInfo == null || !userInfo.canHaveProfile()) { + return false; + } + if (!userInfo.isAdmin()) { + return false; + } + } + DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService( + Context.DEVICE_POLICY_SERVICE); + // restricted profile can be created if there is no DO set and the admin user has no PO + return dpm.getDeviceOwner() == null && dpm.getProfileOwnerAsUser(userId) == null; + } + /* * Should be locked on mUsers before calling this. */ @@ -848,6 +866,20 @@ public class UserManagerService extends IUserManager.Stub { userVersion = 5; } + if (userVersion < 6) { + final boolean splitSystemUser = UserManager.isSplitSystemUser(); + for (int i = 0; i < mUsers.size(); i++) { + UserInfo user = mUsers.valueAt(i); + // In non-split mode, only user 0 can have restricted profiles + if (!splitSystemUser && user.isRestricted() + && (user.restrictedProfileParentId == UserInfo.NO_PROFILE_GROUP_ID)) { + user.restrictedProfileParentId = UserHandle.USER_SYSTEM; + scheduleWriteUserLocked(user); + } + } + userVersion = 6; + } + if (userVersion < USER_VERSION) { Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to " + USER_VERSION); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 5c639330831b..d6d77e9c2608 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -247,7 +247,7 @@ class DisplayContent { for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { final Task task = tasks.get(taskNdx); task.getBounds(mTmpRect); - if (task.inFreeformWorkspace() && mTmpRect.contains(x, y)) { + if (mTmpRect.contains(x, y)) { return task.mTaskId; } } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index d1111f70346a..d467b4f4bf4a 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -17,9 +17,11 @@ package com.android.server.wm; import static android.app.ActivityManager.DOCKED_STACK_ID; +import static android.app.ActivityManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION; import static com.android.server.wm.WindowManagerService.TAG; import static com.android.server.wm.WindowManagerService.DEBUG_RESIZE; import static com.android.server.wm.WindowManagerService.DEBUG_STACK; +import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK; import static android.app.ActivityManager.FREEFORM_WORKSPACE_STACK_ID; import android.content.res.Configuration; @@ -274,7 +276,13 @@ class Task implements DimLayer.DimLayerUser { // this happens, so update the task bounds so it stays in the same place. mTmpRect2.set(mBounds); displayContent.rotateBounds(mRotation, newRotation, mTmpRect2); - setBounds(mTmpRect2, mOverrideConfig); + if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) { + // Post message to inform activity manager of the bounds change simulating + // a one-way call. We do this to prevent a deadlock between window manager + // lock and activity manager lock been held. + mService.mH.sendMessage(mService.mH.obtainMessage( + RESIZE_TASK, mTaskId, RESIZE_MODE_SYSTEM_SCREEN_ROTATION, mBounds)); + } } /** Updates the dim layer bounds, recreating it if needed. */ diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 3b0828441b47..8053fedcebd4 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.app.ActivityManager.*; import static com.android.server.wm.WindowManagerService.DEBUG_TASK_MOVEMENT; +import static com.android.server.wm.WindowManagerService.H.RESIZE_STACK; import static com.android.server.wm.WindowManagerService.TAG; import android.annotation.IntDef; @@ -187,6 +188,9 @@ public class TaskStack implements DimLayer.DimLayerUser { void updateDisplayInfo(Rect bounds) { if (mDisplayContent != null) { + for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { + mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent); + } if (bounds != null) { setBounds(bounds); } else if (mFullscreen) { @@ -195,10 +199,13 @@ public class TaskStack implements DimLayer.DimLayerUser { TmpRect2.set(mBounds); mDisplayContent.rotateBounds( mRotation, mDisplayContent.getDisplayInfo().rotation, TmpRect2); - setBounds(TmpRect2); - } - for (int taskNdx = mTasks.size() - 1; taskNdx >= 0; --taskNdx) { - mTasks.get(taskNdx).updateDisplayInfo(mDisplayContent); + if (setBounds(TmpRect2)) { + // Post message to inform activity manager of the bounds change simulating + // a one-way call. We do this to prevent a deadlock between window manager + // lock and activity manager lock been held. + mService.mH.sendMessage( + mService.mH.obtainMessage(RESIZE_STACK, mStackId, -1, mBounds)); + } } } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ea26822312e2..926bc0f4ef91 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2175,10 +2175,7 @@ public class WindowManagerService extends IWindowManager.Stub // The exit animation is running... wait for it! win.mExiting = true; win.mRemoveOnExit = true; - final DisplayContent displayContent = win.getDisplayContent(); - if (displayContent != null) { - displayContent.layoutNeeded = true; - } + win.setDisplayLayoutNeeded(); final boolean focusChanged = updateFocusedWindowLocked( UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/); mWindowPlacerLocked.performSurfacePlacement(); @@ -2301,10 +2298,7 @@ public class WindowManagerService extends IWindowManager.Stub windows.remove(win); if (!mWindowPlacerLocked.isInLayout()) { assignLayersLocked(windows); - final DisplayContent displayContent = win.getDisplayContent(); - if (displayContent != null) { - displayContent.layoutNeeded = true; - } + win.setDisplayLayoutNeeded(); if (performLayout) { mWindowPlacerLocked.performSurfacePlacement(); } @@ -2386,10 +2380,7 @@ public class WindowManagerService extends IWindowManager.Stub w.mGivenVisibleInsets.scale(w.mGlobalScale); w.mGivenTouchableRegion.scale(w.mGlobalScale); } - final DisplayContent displayContent = w.getDisplayContent(); - if (displayContent != null) { - displayContent.layoutNeeded = true; - } + w.setDisplayLayoutNeeded(); mWindowPlacerLocked.performSurfacePlacement(); } } @@ -2731,10 +2722,7 @@ public class WindowManagerService extends IWindowManager.Stub WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; } - final DisplayContent displayContent = win.getDisplayContent(); - if (displayContent != null) { - displayContent.layoutNeeded = true; - } + win.setDisplayLayoutNeeded(); win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; configChanged = updateOrientationFromAppTokensLocked(false); mWindowPlacerLocked.performSurfacePlacement(); @@ -2827,10 +2815,7 @@ public class WindowManagerService extends IWindowManager.Stub getDefaultDisplayContentLocked().pendingLayoutChanges |= WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; } - final DisplayContent displayContent = win.getDisplayContent(); - if (displayContent != null) { - displayContent.layoutNeeded = true; - } + win.setDisplayLayoutNeeded(); mWindowPlacerLocked.requestTraversal(); } } @@ -3929,10 +3914,7 @@ public class WindowManagerService extends IWindowManager.Stub } } changed = true; - final DisplayContent displayContent = win.getDisplayContent(); - if (displayContent != null) { - displayContent.layoutNeeded = true; - } + win.setDisplayLayoutNeeded(); } } else if (win.isVisibleNow()) { if (!runningAppAnimation) { @@ -3946,10 +3928,7 @@ public class WindowManagerService extends IWindowManager.Stub } } changed = true; - final DisplayContent displayContent = win.getDisplayContent(); - if (displayContent != null) { - displayContent.layoutNeeded = true; - } + win.setDisplayLayoutNeeded(); } } @@ -4117,10 +4096,7 @@ public class WindowManagerService extends IWindowManager.Stub } w.mLastFreezeDuration = 0; unfrozeWindows = true; - final DisplayContent displayContent = w.getDisplayContent(); - if (displayContent != null) { - displayContent.layoutNeeded = true; - } + w.setDisplayLayoutNeeded(); } } if (force || unfrozeWindows) { @@ -7209,6 +7185,9 @@ public class WindowManagerService extends IWindowManager.Stub public static final int UPDATE_DOCKED_STACK_DIVIDER = 42; + public static final int RESIZE_STACK = 43; + public static final int RESIZE_TASK = 44; + @Override public void handleMessage(Message msg) { if (DEBUG_WINDOW_TRACE) { @@ -7755,6 +7734,22 @@ public class WindowManagerService extends IWindowManager.Stub } } break; + case RESIZE_TASK: { + try { + mActivityManager.resizeTask(msg.arg1, (Rect) msg.obj, msg.arg2); + } catch (RemoteException e) { + // This will not happen since we are in the same process. + } + } + break; + case RESIZE_STACK: { + try { + mActivityManager.resizeStack(msg.arg1, (Rect) msg.obj); + } catch (RemoteException e) { + // This will not happen since we are in the same process. + } + } + break; } if (DEBUG_WINDOW_TRACE) { Slog.v(TAG, "handleMessage: exit"); @@ -10083,8 +10078,6 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void saveLastInputMethodWindowForTransition() { synchronized (mWindowMap) { - // TODO(multidisplay): Pass in the displayID. - DisplayContent displayContent = getDefaultDisplayContentLocked(); if (mInputMethodWindow != null) { mPolicy.setLastInputMethodWindowLw(mInputMethodWindow, mInputMethodTarget); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index ad9c3826f494..55ddbc06d959 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1323,6 +1323,12 @@ final class WindowState implements WindowManagerPolicy.WindowState { } } + void setDisplayLayoutNeeded() { + if (mDisplayContent != null) { + mDisplayContent.layoutNeeded = true; + } + } + private class DeathRecipient implements IBinder.DeathRecipient { @Override public void binderDied() { diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java index 3acd56560f4a..982bae0ccb42 100644 --- a/services/net/java/android/net/ip/IpReachabilityMonitor.java +++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java @@ -60,6 +60,74 @@ import java.util.Set; * Monitors on-link IP reachability and notifies callers whenever any on-link * addresses of interest appear to have become unresponsive. * + * This code does not concern itself with "why" a neighbour might have become + * unreachable. Instead, it primarily reacts to the kernel's notion of IP + * reachability for each of the neighbours we know to be critically important + * to normal network connectivity. As such, it is often "just the messenger": + * the neighbours about which it warns are already deemed by the kernel to have + * become unreachable. + * + * + * How it works: + * + * 1. The "on-link neighbours of interest" found in a given LinkProperties + * instance are added to a "watch list" via #updateLinkProperties(). + * This usually means all default gateways and any on-link DNS servers. + * + * 2. We listen continuously for netlink neighbour messages (RTM_NEWNEIGH, + * RTM_DELNEIGH), watching only for neighbours in the watch list. + * + * - A neighbour going into NUD_REACHABLE, NUD_STALE, NUD_DELAY, and + * even NUD_PROBE is perfectly normal; we merely record the new state. + * + * - A neighbour's entry may be deleted (RTM_DELNEIGH), for example due + * to garbage collection. This is not necessarily of immediate + * concern; we record the neighbour as moving to NUD_NONE. + * + * - A neighbour transitioning to NUD_FAILED (for any reason) is + * critically important and is handled as described below in #4. + * + * 3. All on-link neighbours in the watch list can be forcibly "probed" by + * calling #probeAll(). This should be called whenever it is important to + * verify that critical neighbours on the link are still reachable, e.g. + * when roaming between BSSIDs. + * + * - The kernel will send unicast ARP requests for IPv4 neighbours and + * unicast NS packets for IPv6 neighbours. The expected replies will + * likely be unicast. + * + * - The forced probing is done holding a wakelock. The kernel may, + * however, initiate probing of a neighbor on its own, i.e. whenever + * a neighbour has expired from NUD_DELAY. + * + * - The kernel sends: + * + * /proc/sys/net/ipv{4,6}/neigh/<ifname>/ucast_solicit + * + * number of probes (usually 3) every: + * + * /proc/sys/net/ipv{4,6}/neigh/<ifname>/retrans_time_ms + * + * number of milliseconds (usually 1000ms). This normally results in + * 3 unicast packets, 1 per second. + * + * - If no response is received to any of the probe packets, the kernel + * marks the neighbour as being in state NUD_FAILED, and the listening + * process in #2 will learn of it. + * + * 4. We call the supplied Callback#notifyLost() function if the loss of a + * neighbour in NUD_FAILED would cause IPv4 or IPv6 configuration to + * become incomplete (a loss of provisioning). + * + * - For example, losing all our IPv4 on-link DNS servers (or losing + * our only IPv6 default gateway) constitutes a loss of IPv4 (IPv6) + * provisioning; Callback#notifyLost() would be called. + * + * - Since it can be non-trivial to reacquire certain IP provisioning + * state it may be best for the link to disconnect completely and + * reconnect afresh. + * + * * @hide */ public class IpReachabilityMonitor { diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index 1ff621a6cac9..17c24af8992c 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -63,8 +63,14 @@ import java.util.List; public class MockPackageManager extends PackageManager { @Override - public PackageInfo getPackageInfo(String packageName, int flags) - throws NameNotFoundException { + public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException { + throw new UnsupportedOperationException(); + } + + /** @hide */ + @Override + public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) + throws NameNotFoundException { throw new UnsupportedOperationException(); } @@ -524,6 +530,14 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** @hide */ + @Override + public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer, + int flags, String installerPackageName, int userId) { + throw new UnsupportedOperationException(); + } + + @Override public void setInstallerPackageName(String targetPackage, String installerPackageName) { @@ -629,6 +643,15 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + /** + * @hide - to match hiding in superclass + */ + @Override + public void deletePackageAsUser( + String packageName, IPackageDeleteObserver observer, int flags, int userId) { + throw new UnsupportedOperationException(); + } + @Override public void addPackageToPreferred(String packageName) { throw new UnsupportedOperationException(); @@ -792,7 +815,15 @@ public class MockPackageManager extends PackageManager { * @hide */ @Override - public int installExistingPackage(String packageName) + public int installExistingPackage(String packageName) throws NameNotFoundException { + throw new UnsupportedOperationException(); + } + + /** + * @hide + */ + @Override + public int installExistingPackageAsUser(String packageName, int userId) throws NameNotFoundException { throw new UnsupportedOperationException(); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java index f04654eded0f..e3a19e76a3ef 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java @@ -65,6 +65,12 @@ public class BridgePackageManager extends PackageManager { } @Override + public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId) + throws NameNotFoundException { + return null; + } + + @Override public String[] currentToCanonicalPackageNames(String[] names) { return new String[0]; } @@ -499,6 +505,11 @@ public class BridgePackageManager extends PackageManager { } @Override + public void installPackageAsUser(Uri packageURI, PackageInstallObserver observer,int flags, + String installerPackageName, int userId) { + } + + @Override public void installPackageWithVerification(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, Uri verificationURI, ManifestDigest manifestDigest, ContainerEncryptionParams encryptionParams) { @@ -516,6 +527,12 @@ public class BridgePackageManager extends PackageManager { } @Override + public int installExistingPackageAsUser(String packageName, int userId) + throws NameNotFoundException { + return 0; + } + + @Override public void verifyPendingInstall(int id, int verificationCode) { } @@ -568,6 +585,11 @@ public class BridgePackageManager extends PackageManager { } @Override + public void deletePackageAsUser(String packageName, IPackageDeleteObserver observer, int flags, + int userId) { + } + + @Override public String getInstallerPackageName(String packageName) { return null; } |