diff options
738 files changed, 20216 insertions, 17063 deletions
diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 48277fb3b488..2df158d1b565 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -482,8 +482,6 @@ package android.app { field public static final int WINDOWING_MODE_FULLSCREEN = 1; // 0x1 field public static final int WINDOWING_MODE_MULTI_WINDOW = 6; // 0x6 field public static final int WINDOWING_MODE_PINNED = 2; // 0x2 - field public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3; // 0x3 - field public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4; // 0x4 field public static final int WINDOWING_MODE_UNDEFINED = 0; // 0x0 } @@ -1857,7 +1855,6 @@ package android.os { method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle); - method public static boolean isGuestUserEphemeral(); method public static boolean isSplitSystemUser(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException; } @@ -3396,7 +3393,7 @@ package android.window { method @NonNull public android.window.WindowContainerTransaction reparentTasks(@Nullable android.window.WindowContainerToken, @Nullable android.window.WindowContainerToken, @Nullable int[], @Nullable int[], boolean); method @NonNull public android.window.WindowContainerTransaction scheduleFinishEnterPip(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect); method @NonNull public android.window.WindowContainerTransaction setActivityWindowingMode(@NonNull android.window.WindowContainerToken, int); - method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken, boolean); + method @NonNull public android.window.WindowContainerTransaction setAdjacentRoots(@NonNull android.window.WindowContainerToken, @NonNull android.window.WindowContainerToken); method @NonNull public android.window.WindowContainerTransaction setAdjacentTaskFragments(@NonNull android.os.IBinder, @Nullable android.os.IBinder, @Nullable android.window.WindowContainerTransaction.TaskFragmentAdjacentParams); method @NonNull public android.window.WindowContainerTransaction setAppBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect); method @NonNull public android.window.WindowContainerTransaction setBounds(@NonNull android.window.WindowContainerToken, @NonNull android.graphics.Rect); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index ae16e01b7788..7141259d7dce 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -8260,6 +8260,11 @@ public class Activity extends ContextThemeWrapper return mMainThread; } + /** @hide */ + public final ActivityInfo getActivityInfo() { + return mActivityInfo; + } + final void performCreate(Bundle icicle) { performCreate(icicle, null); } diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 5d1d225f4d2d..e25e374122cf 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -2210,7 +2210,6 @@ public class ActivityManager { pw.print(((baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0)); pw.print(" activityType="); pw.print(activityTypeToString(getActivityType())); pw.print(" windowingMode="); pw.print(windowingModeToString(getWindowingMode())); - pw.print(" supportsSplitScreenMultiWindow="); pw.print(supportsSplitScreenMultiWindow); pw.print(" supportsMultiWindow="); pw.println(supportsMultiWindow); if (taskDescription != null) { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 1d1743f56ff7..9e9e985d27de 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -3431,26 +3431,12 @@ public final class ActivityThread extends ClientTransactionHandler } } - /** - * Returns {@code true} if the {@link android.app.ActivityManager.ProcessState} of the current - * process is cached. - */ - @Override - @VisibleForTesting - public boolean isCachedProcessState() { - synchronized (mAppThread) { - return mLastProcessState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; - } - } - @Override public void updateProcessState(int processState, boolean fromIpc) { - final boolean wasCached; synchronized (mAppThread) { if (mLastProcessState == processState) { return; } - wasCached = isCachedProcessState(); mLastProcessState = processState; // Defer the top state for VM to avoid aggressive JIT compilation affecting activity // launch time. @@ -3467,22 +3453,6 @@ public final class ActivityThread extends ClientTransactionHandler + (fromIpc ? " (from ipc" : "")); } } - - // Handle the pending configuration if the process state is changed from cached to - // non-cached. Except the case where there is a launching activity because the - // LaunchActivityItem will handle it. - if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) { - final Configuration pendingConfig = - mConfigurationController.getPendingConfiguration(false /* clearPending */); - if (pendingConfig == null) { - return; - } - if (Looper.myLooper() == mH.getLooper()) { - handleConfigurationChanged(pendingConfig); - } else { - sendMessage(H.CONFIGURATION_CHANGED, pendingConfig); - } - } } /** Update VM state based on ActivityManager.PROCESS_STATE_* constants. */ diff --git a/core/java/android/app/ActivityThreadInternal.java b/core/java/android/app/ActivityThreadInternal.java index b9ad5c337813..72506b9fcdbb 100644 --- a/core/java/android/app/ActivityThreadInternal.java +++ b/core/java/android/app/ActivityThreadInternal.java @@ -32,8 +32,6 @@ interface ActivityThreadInternal { boolean isInDensityCompatMode(); - boolean isCachedProcessState(); - Application getApplication(); ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean includeUiContexts); diff --git a/core/java/android/app/ConfigurationController.java b/core/java/android/app/ConfigurationController.java index 1a77b65c8ef6..18dc1ce18baf 100644 --- a/core/java/android/app/ConfigurationController.java +++ b/core/java/android/app/ConfigurationController.java @@ -124,12 +124,6 @@ class ConfigurationController { * @param config The new configuration. */ void handleConfigurationChanged(@NonNull Configuration config) { - if (mActivityThread.isCachedProcessState()) { - updatePendingConfiguration(config); - // If the process is in a cached state, delay the handling until the process is no - // longer cached. - return; - } Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "configChanged"); handleConfigurationChanged(config, null /* compat */); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 2a1883d5e0bb..d275c8336251 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -365,8 +365,8 @@ public class ResourcesManager { @NonNull Configuration config) { config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH; config.densityDpi = dm.densityDpi; - config.screenWidthDp = (int) (dm.widthPixels / dm.density); - config.screenHeightDp = (int) (dm.heightPixels / dm.density); + config.screenWidthDp = (int) (dm.widthPixels / dm.density + 0.5f); + config.screenHeightDp = (int) (dm.heightPixels / dm.density + 0.5f); int sl = Configuration.resetScreenLayout(config.screenLayout); if (dm.widthPixels > dm.heightPixels) { config.orientation = Configuration.ORIENTATION_LANDSCAPE; diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 7910f1a426c2..b09463e3074b 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -140,13 +140,6 @@ public class TaskInfo { public LocusId mTopActivityLocusId; /** - * True if the task can go in the split-screen primary stack. - * @hide - */ - @UnsupportedAppUsage - public boolean supportsSplitScreenMultiWindow; - - /** * Whether this task supports multi windowing modes based on the device settings and the * root activity resizability and configuration. * @hide @@ -499,7 +492,6 @@ public class TaskInfo { lastActiveTime = source.readLong(); taskDescription = source.readTypedObject(ActivityManager.TaskDescription.CREATOR); - supportsSplitScreenMultiWindow = source.readBoolean(); supportsMultiWindow = source.readBoolean(); resizeMode = source.readInt(); configuration.readFromParcel(source); @@ -546,7 +538,6 @@ public class TaskInfo { dest.writeLong(lastActiveTime); dest.writeTypedObject(taskDescription, flags); - dest.writeBoolean(supportsSplitScreenMultiWindow); dest.writeBoolean(supportsMultiWindow); dest.writeInt(resizeMode); configuration.writeToParcel(dest, flags); @@ -584,7 +575,6 @@ public class TaskInfo { + " realActivity=" + realActivity + " numActivities=" + numActivities + " lastActiveTime=" + lastActiveTime - + " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow + " supportsMultiWindow=" + supportsMultiWindow + " resizeMode=" + resizeMode + " isResizeable=" + isResizeable diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index e96a98697184..d0ea8d41d65c 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -104,19 +104,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu public static final int WINDOWING_MODE_FULLSCREEN = 1; /** Always on-top (always visible). of other siblings in its parent container. */ public static final int WINDOWING_MODE_PINNED = 2; - /** The primary container driving the screen to be in split-screen mode. */ - // TODO: Remove once split-screen is migrated to wm-shell. - public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3; - /** - * The containers adjacent to the {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} container in - * split-screen mode. - * NOTE: Containers launched with the windowing mode with APIs like - * {@link ActivityOptions#setLaunchWindowingMode(int)} will be launched in - * {@link #WINDOWING_MODE_FULLSCREEN} if the display isn't currently in split-screen windowing - * mode - */ - // TODO: Remove once split-screen is migrated to wm-shell. - public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4; /** Can be freely resized within its parent container. */ // TODO: Remove once freeform is migrated to wm-shell. public static final int WINDOWING_MODE_FREEFORM = 5; @@ -129,8 +116,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_MULTI_WINDOW, WINDOWING_MODE_PINNED, - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, WINDOWING_MODE_FREEFORM, }) public @interface WindowingMode {} @@ -890,9 +875,8 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu } /** - * Returns true if this container can be put in either - * {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or - * {@link #WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on its current state. + * Returns true if this container can be put in {@link #WINDOWING_MODE_MULTI_WINDOW} + * windowing mode based on its current state. * @hide */ public boolean supportSplitScreenWindowingMode() { @@ -911,8 +895,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu case WINDOWING_MODE_FULLSCREEN: return "fullscreen"; case WINDOWING_MODE_MULTI_WINDOW: return "multi-window"; case WINDOWING_MODE_PINNED: return "pinned"; - case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return "split-screen-primary"; - case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return "split-screen-secondary"; case WINDOWING_MODE_FREEFORM: return "freeform"; } return String.valueOf(windowingMode); diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 0673b3ad5b0a..a3d595ef6575 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -106,6 +106,24 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public @interface LaunchMode { } + /** @hide */ + public static String launchModeToString(@LaunchMode int launchMode) { + switch(launchMode) { + case LAUNCH_MULTIPLE: + return "LAUNCH_MULTIPLE"; + case LAUNCH_SINGLE_TOP: + return "LAUNCH_SINGLE_TOP"; + case LAUNCH_SINGLE_TASK: + return "LAUNCH_SINGLE_TASK"; + case LAUNCH_SINGLE_INSTANCE: + return "LAUNCH_SINGLE_INSTANCE"; + case LAUNCH_SINGLE_INSTANCE_PER_TASK: + return "LAUNCH_SINGLE_INSTANCE_PER_TASK"; + default: + return "unknown=" + launchMode; + } + } + /** * The launch mode style requested by the activity. From the * {@link android.R.attr#launchMode} attribute. @@ -1585,7 +1603,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { + " persistableMode=" + persistableModeToString()); } if (launchMode != 0 || flags != 0 || privateFlags != 0 || theme != 0) { - pw.println(prefix + "launchMode=" + launchMode + pw.println(prefix + "launchMode=" + launchModeToString(launchMode) + " flags=0x" + Integer.toHexString(flags) + " privateFlags=0x" + Integer.toHexString(privateFlags) + " theme=0x" + Integer.toHexString(theme)); diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java index d6e13ac90f82..9baa6ba2fb49 100644 --- a/core/java/android/content/pm/UserInfo.java +++ b/core/java/android/content/pm/UserInfo.java @@ -143,6 +143,22 @@ public class UserInfo implements Parcelable { public static final int FLAG_PROFILE = 0x00001000; /** + * Indicates that this user is created in ephemeral mode via + * {@link IUserManager} create user. + * + * When a user is created with {@link #FLAG_EPHEMERAL}, {@link #FLAG_EPHEMERAL_ON_CREATE} + * is set internally within the user manager. + * + * When {@link #FLAG_EPHEMERAL_ON_CREATE} is set {@link IUserManager.setUserEphemeral} + * has no effect because a user that was created ephemeral can never be made non-ephemeral. + * + * {@link #FLAG_EPHEMERAL_ON_CREATE} should NOT be set by client's of user manager + * + * @hide + */ + public static final int FLAG_EPHEMERAL_ON_CREATE = 0x00002000; + + /** * @hide */ @IntDef(flag = true, prefix = "FLAG_", value = { @@ -158,7 +174,8 @@ public class UserInfo implements Parcelable { FLAG_DEMO, FLAG_FULL, FLAG_SYSTEM, - FLAG_PROFILE + FLAG_PROFILE, + FLAG_EPHEMERAL_ON_CREATE }) @Retention(RetentionPolicy.SOURCE) public @interface UserInfoFlag { diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java index 7d8f2ff92200..8c71b363eb7b 100644 --- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java +++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java @@ -89,7 +89,8 @@ public class AmbientDisplayConfiguration { /** @hide */ public boolean pulseOnNotificationAvailable() { - return ambientDisplayAvailable(); + return mContext.getResources().getBoolean(R.bool.config_pulseOnNotificationsAvailable) + && ambientDisplayAvailable(); } /** @hide */ diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl index e1ccc4fb740b..170df71385bb 100644 --- a/core/java/android/nfc/INfcTag.aidl +++ b/core/java/android/nfc/INfcTag.aidl @@ -46,6 +46,5 @@ interface INfcTag int getMaxTransceiveLength(int technology); boolean getExtendedLengthApdusSupported(); - void setTagUpToDate(long cookie); boolean isTagUpToDate(long cookie); } diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java index 731d1ba78299..500038f14d9d 100644 --- a/core/java/android/nfc/Tag.java +++ b/core/java/android/nfc/Tag.java @@ -34,7 +34,6 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; -import android.os.SystemClock; import java.io.IOException; import java.util.Arrays; @@ -119,17 +118,17 @@ public final class Tag implements Parcelable { final String[] mTechStringList; final Bundle[] mTechExtras; final int mServiceHandle; // for use by NFC service, 0 indicates a mock + final long mCookie; // for accessibility checking final INfcTag mTagService; // interface to NFC service, will be null if mock tag int mConnectedTechnology; - long mCookie; /** * Hidden constructor to be used by NFC service and internal classes. * @hide */ public Tag(byte[] id, int[] techList, Bundle[] techListExtras, int serviceHandle, - INfcTag tagService) { + long cookie, INfcTag tagService) { if (techList == null) { throw new IllegalArgumentException("rawTargets cannot be null"); } @@ -139,20 +138,13 @@ public final class Tag implements Parcelable { // Ensure mTechExtras is as long as mTechList mTechExtras = Arrays.copyOf(techListExtras, techList.length); mServiceHandle = serviceHandle; + mCookie = cookie; mTagService = tagService; - mConnectedTechnology = -1; - mCookie = SystemClock.elapsedRealtime(); if (tagService == null) { return; } - - try { - tagService.setTagUpToDate(mCookie); - } catch (RemoteException e) { - throw e.rethrowAsRuntimeException(); - } } /** @@ -165,9 +157,10 @@ public final class Tag implements Parcelable { * @return freshly constructed tag * @hide */ - public static Tag createMockTag(byte[] id, int[] techList, Bundle[] techListExtras) { + public static Tag createMockTag(byte[] id, int[] techList, Bundle[] techListExtras, + long cookie) { // set serviceHandle to 0 and tagService to null to indicate mock tag - return new Tag(id, techList, techListExtras, 0, null); + return new Tag(id, techList, techListExtras, 0, cookie, null); } private String[] generateTechStringList(int[] techList) { @@ -445,6 +438,7 @@ public final class Tag implements Parcelable { dest.writeIntArray(mTechList); dest.writeTypedArray(mTechExtras, 0); dest.writeInt(mServiceHandle); + dest.writeLong(mCookie); dest.writeInt(isMock); if (isMock == 0) { dest.writeStrongBinder(mTagService.asBinder()); @@ -463,6 +457,7 @@ public final class Tag implements Parcelable { in.readIntArray(techList); Bundle[] techExtras = in.createTypedArray(Bundle.CREATOR); int serviceHandle = in.readInt(); + long cookie = in.readLong(); int isMock = in.readInt(); if (isMock == 0) { tagService = INfcTag.Stub.asInterface(in.readStrongBinder()); @@ -471,7 +466,7 @@ public final class Tag implements Parcelable { tagService = null; } - return new Tag(id, techList, techExtras, serviceHandle, tagService); + return new Tag(id, techList, techExtras, serviceHandle, cookie, tagService); } @Override diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 3cde0319efd3..e5de3e157c88 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -131,4 +131,5 @@ interface IUserManager { String getUserName(); long getUserStartRealtime(); long getUserUnlockRealtime(); + boolean setUserEphemeral(int userId, boolean enableEphemeral); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 0ffdfc6cbcb1..d6566048f2c4 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -2001,13 +2001,22 @@ public class UserManager { * @return Whether guest user is always ephemeral * @hide */ - @TestApi - public static boolean isGuestUserEphemeral() { + public static boolean isGuestUserAlwaysEphemeral() { return Resources.getSystem() .getBoolean(com.android.internal.R.bool.config_guestUserEphemeral); } /** + * @return true, when we want to enable user manager API and UX to allow + * guest user ephemeral state change based on user input + * @hide + */ + public static boolean isGuestUserAllowEphemeralStateChange() { + return Resources.getSystem() + .getBoolean(com.android.internal.R.bool.config_guestUserAllowEphemeralStateChange); + } + + /** * Checks whether the device is running in a headless system user mode. * * <p>Headless system user mode means the {@link #isSystemUser() system user} runs system @@ -3431,6 +3440,20 @@ public class UserManager { if (guest != null) { Settings.Secure.putStringForUser(context.getContentResolver(), Settings.Secure.SKIP_FIRST_USE_HINTS, "1", guest.id); + + if (UserManager.isGuestUserAllowEphemeralStateChange()) { + // Mark guest as (changeably) ephemeral if REMOVE_GUEST_ON_EXIT is 1 + // This is done so that a user via a UI controller can choose to + // make a guest as ephemeral or not. + // Settings.Global.REMOVE_GUEST_ON_EXIT holds the choice on what the guest state + // should be, with default being ephemeral. + boolean resetGuestOnExit = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.REMOVE_GUEST_ON_EXIT, 1) == 1; + + if (resetGuestOnExit && !guest.isEphemeral()) { + setUserEphemeral(guest.id, true); + } + } } return guest; } catch (ServiceSpecificException e) { @@ -4948,6 +4971,31 @@ public class UserManager { } /** + * Set the user as ephemeral or non-ephemeral. + * + * If the user was initially created as ephemeral then this + * method has no effect and false is returned. + * + * @param userId the user's integer id + * @param enableEphemeral true: change user state to ephemeral, + * false: change user state to non-ephemeral + * @return true: user now has the desired ephemeral state, + * false: desired user ephemeral state could not be set + * + * @hide + */ + @RequiresPermission(anyOf = { + android.Manifest.permission.MANAGE_USERS, + android.Manifest.permission.CREATE_USERS}) + public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) { + try { + return mService.setUserEphemeral(userId, enableEphemeral); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Updates the context user's name. * * @param name the new name for the user diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 805fdc44b256..7964f7cd6908 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9141,6 +9141,57 @@ public final class Settings { public static final String SCREENSAVER_ENABLED_COMPLICATIONS = "screensaver_enabled_complications"; + + /** + * Default, indicates that the user has not yet started the dock setup flow. + * + * @hide + */ + public static final int DOCK_SETUP_NOT_STARTED = 0; + + /** + * Indicates that the user has started but not yet completed dock setup. + * One of the possible states for {@link #DOCK_SETUP_STATE}. + * + * @hide + */ + public static final int DOCK_SETUP_STARTED = 1; + + /** + * Indicates that the user has snoozed dock setup and will complete it later. + * One of the possible states for {@link #DOCK_SETUP_STATE}. + * + * @hide + */ + public static final int DOCK_SETUP_PAUSED = 2; + + /** + * Indicates that the user has completed dock setup. + * One of the possible states for {@link #DOCK_SETUP_STATE}. + * + * @hide + */ + public static final int DOCK_SETUP_COMPLETED = 10; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({ + DOCK_SETUP_NOT_STARTED, + DOCK_SETUP_STARTED, + DOCK_SETUP_PAUSED, + DOCK_SETUP_COMPLETED + }) + public @interface DockSetupState { + } + + /** + * Defines the user's current state of dock setup. + * The possible states are defined in {@link DockSetupState}. + * + * @hide + */ + public static final String DOCK_SETUP_STATE = "dock_setup_state"; + /** * The default NFC payment component * @hide @@ -10820,6 +10871,15 @@ public final class Settings { "launcher_taskbar_education_showing"; /** + * Whether or not adaptive charging feature is enabled by user. + * Type: int (0 for false, 1 for true) + * Default: 1 + * + * @hide + */ + public static final String ADAPTIVE_CHARGING_ENABLED = "adaptive_charging_enabled"; + + /** * These entries are considered common between the personal and the managed profile, * since the managed profile doesn't get to change them. */ @@ -10945,6 +11005,14 @@ public final class Settings { public static final String ADD_USERS_WHEN_LOCKED = "add_users_when_locked"; /** + * Whether guest user should be removed on exit from guest mode. + * <p> + * Type: int + * @hide + */ + public static final String REMOVE_GUEST_ON_EXIT = "remove_guest_on_exit"; + + /** * Whether applying ramping ringer on incoming phone call ringtone. * <p>1 = apply ramping ringer * <p>0 = do not apply ramping ringer diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java index bf64d06d4ff0..f6a7c8eb8c4b 100644 --- a/core/java/android/service/dreams/DreamActivity.java +++ b/core/java/android/service/dreams/DreamActivity.java @@ -21,8 +21,6 @@ import android.app.Activity; import android.os.Bundle; import android.text.TextUtils; -import com.android.internal.R; - /** * The Activity used by the {@link DreamService} to draw screensaver content * on the screen. This activity runs in dream application's process, but is started by a @@ -66,17 +64,4 @@ public class DreamActivity extends Activity { callback.onActivityCreated(this); } } - - @Override - public void onResume() { - super.onResume(); - overridePendingTransition(R.anim.dream_activity_open_enter, - R.anim.dream_activity_open_exit); - } - - @Override - public void finishAndRemoveTask() { - super.finishAndRemoveTask(); - overridePendingTransition(0, R.anim.dream_activity_close_exit); - } } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index d598017dacaa..5f5645f9e3cb 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -1536,8 +1536,9 @@ public abstract class WallpaperService extends Service { // may have been destroyed so now we need to make // sure it is re-created. doOffsetsChanged(false); - // force relayout to get new surface - updateSurface(true, false, false); + // It will check mSurfaceCreated so no need to force relayout. + updateSurface(false /* forceRelayout */, false /* forceReport */, + false /* redrawNeeded */); } onVisibilityChanged(visible); if (mReportedVisible && mFrozenRequested) { diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 24ded932b636..6049199fc217 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -83,6 +83,11 @@ public class FeatureFlagUtils { public static final String SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE = "settings_hide_second_layer_page_navigate_up_button_in_two_pane"; + /** Flag to enable/disable guest mode UX changes as mentioned in b/214031645 + * @hide + */ + public static final String SETTINGS_GUEST_MODE_UX_CHANGES = "settings_guest_mode_ux_changes"; + private static final Map<String, String> DEFAULT_FLAGS; static { @@ -110,6 +115,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true"); DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true"); DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true"); + DEFAULT_FLAGS.put(SETTINGS_GUEST_MODE_UX_CHANGES, "true"); } private static final Set<String> PERSISTENT_FLAGS; diff --git a/core/java/android/view/IDisplayWindowRotationCallback.aidl b/core/java/android/view/IDisplayChangeWindowCallback.aidl index 1ffe2dde40f1..00a5b7b831ca 100644 --- a/core/java/android/view/IDisplayWindowRotationCallback.aidl +++ b/core/java/android/view/IDisplayChangeWindowCallback.aidl @@ -17,13 +17,15 @@ package android.view; import android.window.WindowContainerTransaction; +import android.window.DisplayAreaInfo; /** - * Interface to be invoked by the controller when it has finished preparing for a display rotation. + * Interface to be invoked by the controller when it has finished preparing for a display + * size change. * - * @see IDisplayWindowRotationController + * @see IDisplayChangeWindowController * @hide */ -interface IDisplayWindowRotationCallback { - void continueRotateDisplay(int targetRotation, in WindowContainerTransaction t); +interface IDisplayChangeWindowCallback { + void continueDisplayChange(in WindowContainerTransaction t); } diff --git a/core/java/android/view/IDisplayWindowRotationController.aidl b/core/java/android/view/IDisplayChangeWindowController.aidl index c1c7464c3168..8c0bb6a54528 100644 --- a/core/java/android/view/IDisplayWindowRotationController.aidl +++ b/core/java/android/view/IDisplayChangeWindowController.aidl @@ -16,11 +16,12 @@ package android.view; -import android.view.IDisplayWindowRotationCallback; +import android.view.IDisplayChangeWindowCallback; +import android.window.DisplayAreaInfo; /** - * Singular controller of a "remote" display rotation. When a display rotation is started, WM - * freezes the screen. It will then call into this controller and wait for a response via the + * Singular controller of a "remote" display change. When a display rotation or change is started, + * WM freezes the screen. It will then call into this controller and wait for a response via the * callback. * * This needs to provide configuration changes because those changes need to be applied in sync @@ -36,17 +37,18 @@ import android.view.IDisplayWindowRotationCallback; * * @hide */ -oneway interface IDisplayWindowRotationController { +oneway interface IDisplayChangeWindowController { /** - * Called when WM needs to know how to update tasks in response to a display rotation. - * If this isn't called, a timeout will continue the rotation in WM. + * Called when WM needs to know how to update tasks in response to a display change. + * If this isn't called, a timeout will continue the change in WM. * - * @param displayId the display that is rotating. - * @param fromRotation the rotation the display is rotating from. - * @param toRotation the rotation the display is rotating to. + * @param fromRotation the old rotation + * @param newRotation the new rotation + * @param newDisplayAreaInfo the new display area info after the change * @param callback A callback to be called when this has calculated updated configs. */ - void onRotateDisplay(int displayId, int fromRotation, int toRotation, - in IDisplayWindowRotationCallback callback); + void onDisplayChange(int displayId, int fromRotation, int toRotation, + in DisplayAreaInfo newDisplayAreaInfo, in IDisplayChangeWindowCallback callback); + } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index fb562d8e97db..acdff4fb52fd 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -39,7 +39,7 @@ import android.view.ICrossWindowBlurEnabledListener; import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; import android.view.IDisplayFoldListener; -import android.view.IDisplayWindowRotationController; +import android.view.IDisplayChangeWindowController; import android.view.IOnKeyguardExitResult; import android.view.IPinnedTaskListener; import android.view.IScrollCaptureResponseListener; @@ -145,7 +145,7 @@ interface IWindowManager * controller is called after the display has "frozen" for a rotation and display rotation will * only continue once the controller has finished calculating associated configurations. */ - void setDisplayWindowRotationController(IDisplayWindowRotationController controller); + void setDisplayChangeWindowController(IDisplayChangeWindowController controller); /** * Adds a root container that a client shell can populate with its own windows (usually via @@ -250,18 +250,6 @@ interface IWindowManager */ void refreshScreenCaptureDisabled(); - // These can only be called with the SET_ORIENTATION permission. - /** - * Update the current screen rotation based on the current state of - * the world. - * @param alwaysSendConfiguration Flag to force a new configuration to - * be evaluated. This can be used when there are other parameters in - * configuration that are changing. - * @param forceRelayout If true, the window manager will always do a relayout - * of its windows even if the rotation hasn't changed. - */ - void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout); - /** * Retrieve the current orientation of the primary screen. * @return Constant as per {@link android.view.Surface.Rotation}. @@ -442,11 +430,6 @@ interface IWindowManager boolean isSafeModeEnabled(); /** - * Enables the screen if all conditions are met. - */ - void enableScreenIfNeeded(); - - /** * Clears the frame statistics for a given window. * * @param token The window token. @@ -560,6 +543,21 @@ interface IWindowManager boolean isWindowTraceEnabled(); /** + * Starts a transition trace. + */ + void startTransitionTrace(); + + /** + * Stops a transition trace. + */ + void stopTransitionTrace(); + + /** + * Returns true if transition trace is enabled. + */ + boolean isTransitionTraceEnabled(); + + /** * Gets the windowing mode of the display. * * @param displayId The id of the display. diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java index f7bca5bfe188..d75ff2fc7dc2 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -29,8 +29,6 @@ import android.os.Parcelable; import android.os.RemoteException; import android.util.Log; import android.window.WindowTokenClient; -import android.view.InsetsState; -import android.view.WindowManagerGlobal; import android.view.accessibility.IAccessibilityEmbeddedConnection; import java.util.Objects; @@ -280,7 +278,7 @@ public class SurfaceControlViewHost { public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d, @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) { mWm = wwm; - mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer); + mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout(), useSfChoreographer); addConfigCallback(c, d); WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot); @@ -310,7 +308,7 @@ public class SurfaceControlViewHost { mWm = new WindowlessWindowManager(context.getResources().getConfiguration(), mSurfaceControl, hostToken); - mViewRoot = new ViewRootImpl(context, display, mWm); + mViewRoot = new ViewRootImpl(context, display, mWm, new WindowlessWindowLayout()); addConfigCallback(context, display); WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 127c7b7a8dc9..19c2cfbacb64 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -563,7 +563,7 @@ public final class ViewRootImpl implements ViewParent, private final Rect mVisRect = new Rect(); // used to retrieve visible rect of focused view. private final Rect mTempRect = new Rect(); - private final WindowLayout mWindowLayout = new WindowLayout(); + private final WindowLayout mWindowLayout; private ViewRootImpl mParentViewRoot; @@ -603,6 +603,7 @@ public final class ViewRootImpl implements ViewParent, int mSyncSeqId = 0; int mLastSyncSeqId = 0; + private boolean mUpdateSurfaceNeeded; boolean mFullRedrawNeeded; boolean mNewSurfaceNeeded; boolean mForceNextWindowRelayout; @@ -880,18 +881,20 @@ public final class ViewRootImpl implements ViewParent, private String mTag = TAG; public ViewRootImpl(Context context, Display display) { - this(context, display, WindowManagerGlobal.getWindowSession(), + this(context, display, WindowManagerGlobal.getWindowSession(), new WindowLayout(), false /* useSfChoreographer */); } - public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session) { - this(context, display, session, false /* useSfChoreographer */); + public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session, + WindowLayout windowLayout) { + this(context, display, session, windowLayout, false /* useSfChoreographer */); } public ViewRootImpl(@UiContext Context context, Display display, IWindowSession session, - boolean useSfChoreographer) { + WindowLayout windowLayout, boolean useSfChoreographer) { mContext = context; mWindowSession = session; + mWindowLayout = windowLayout; mDisplay = display; mBasePackageName = context.getBasePackageName(); mThread = Thread.currentThread(); @@ -1767,7 +1770,7 @@ public final class ViewRootImpl implements ViewParent, } } - mForceNextWindowRelayout = forceNextWindowRelayout; + mForceNextWindowRelayout |= forceNextWindowRelayout; mPendingAlwaysConsumeSystemBars = args.argi2 != 0; mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId; @@ -2893,7 +2896,6 @@ public final class ViewRootImpl implements ViewParent, final int surfaceGenerationId = mSurface.getGenerationId(); final boolean isViewVisible = viewVisibility == View.VISIBLE; - final boolean windowRelayoutWasForced = mForceNextWindowRelayout; boolean surfaceSizeChanged = false; boolean surfaceCreated = false; boolean surfaceDestroyed = false; @@ -2997,6 +2999,8 @@ public final class ViewRootImpl implements ViewParent, !mFirst, INVALID_DISPLAY /* same display */); updatedConfiguration = true; } + final boolean updateSurfaceNeeded = mUpdateSurfaceNeeded; + mUpdateSurfaceNeeded = false; surfaceSizeChanged = false; if (!mLastSurfaceSize.equals(mSurfaceSize)) { @@ -3075,8 +3079,7 @@ public final class ViewRootImpl implements ViewParent, if (isHardwareEnabled()) { mAttachInfo.mThreadedRenderer.destroy(); } - } else if ((surfaceReplaced - || surfaceSizeChanged || windowRelayoutWasForced) + } else if ((surfaceReplaced || surfaceSizeChanged || updateSurfaceNeeded) && mSurfaceHolder == null && mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) { @@ -5204,6 +5207,21 @@ public final class ViewRootImpl implements ViewParent, throw new IllegalArgumentException("No merged config provided."); } + final int lastRotation = mLastReportedMergedConfiguration.getMergedConfiguration() + .windowConfiguration.getRotation(); + final int newRotation = mergedConfiguration.getMergedConfiguration() + .windowConfiguration.getRotation(); + if (lastRotation != newRotation) { + // Trigger ThreadedRenderer#updateSurface() if the surface control doesn't change. + // Because even if the actual surface size is not changed, e.g. flip 180 degrees, + // the buffers may still have content in previous rotation. And the next draw may + // not update all regions, that causes some afterimages to flicker. + mUpdateSurfaceNeeded = true; + if (!mIsInTraversal) { + mForceNextWindowRelayout = true; + } + } + Configuration globalConfig = mergedConfiguration.getGlobalConfiguration(); final Configuration overrideConfig = mergedConfiguration.getOverrideConfiguration(); if (DEBUG_CONFIGURATION) Log.v(mTag, diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 00052f6015d5..690981eaa14a 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -341,6 +341,18 @@ public interface WindowManager extends ViewManager { int TRANSIT_OLD_TASK_FRAGMENT_CHANGE = 30; /** + * A dream activity is being opened. + * @hide + */ + int TRANSIT_OLD_DREAM_ACTIVITY_OPEN = 31; + + /** + * A dream activity is being closed. + * @hide + */ + int TRANSIT_OLD_DREAM_ACTIVITY_CLOSE = 32; + + /** * @hide */ @IntDef(prefix = { "TRANSIT_OLD_" }, value = { @@ -368,7 +380,9 @@ public interface WindowManager extends ViewManager { TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE, TRANSIT_OLD_TASK_FRAGMENT_OPEN, TRANSIT_OLD_TASK_FRAGMENT_CLOSE, - TRANSIT_OLD_TASK_FRAGMENT_CHANGE + TRANSIT_OLD_TASK_FRAGMENT_CHANGE, + TRANSIT_OLD_DREAM_ACTIVITY_OPEN, + TRANSIT_OLD_DREAM_ACTIVITY_CLOSE }) @Retention(RetentionPolicy.SOURCE) @interface TransitionOldType {} diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index aae930edb729..85a5762f7f3d 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -386,7 +386,7 @@ public final class WindowManagerGlobal { root = new ViewRootImpl(view.getContext(), display); } else { root = new ViewRootImpl(view.getContext(), display, - windowlessSession); + windowlessSession, new WindowlessWindowLayout()); } view.setLayoutParams(wparams); diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index 4d07171d3086..43d427db2c75 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -237,10 +237,6 @@ public interface WindowManagerPolicyConstants { */ int LAYER_OFFSET_THUMBNAIL = WINDOW_LAYER_MULTIPLIER - 1; - // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and - // make app pair split only have single root then we can just attach the - // divider to the single root task in shell. - int SPLIT_DIVIDER_LAYER = TYPE_LAYER_MULTIPLIER * 3; int WATERMARK_LAYER = TYPE_LAYER_MULTIPLIER * 100; int STRICT_MODE_LAYER = TYPE_LAYER_MULTIPLIER * 101; int WINDOW_FREEZE_LAYER = TYPE_LAYER_MULTIPLIER * 200; diff --git a/core/java/android/view/WindowlessWindowLayout.java b/core/java/android/view/WindowlessWindowLayout.java new file mode 100644 index 000000000000..7cc50c579d0d --- /dev/null +++ b/core/java/android/view/WindowlessWindowLayout.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 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.view; + +import android.app.WindowConfiguration.WindowingMode; +import android.graphics.Rect; +import android.window.ClientWindowFrames; + +/** + * Computes window frames for the windowless window. + * @hide + */ +public class WindowlessWindowLayout extends WindowLayout { + + @Override + public void computeFrames(WindowManager.LayoutParams attrs, InsetsState state, + Rect displayCutoutSafe, Rect windowBounds, @WindowingMode int windowingMode, + int requestedWidth, int requestedHeight, InsetsVisibilities requestedVisibilities, + Rect attachedWindowFrame, float compatScale, ClientWindowFrames outFrames) { + outFrames.frame.set(0, 0, attrs.width, attrs.height); + outFrames.displayFrame.set(outFrames.frame); + outFrames.parentFrame.set(outFrames.frame); + } +} + diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java index f72164e1f53f..56e910769cb5 100644 --- a/core/java/android/window/TaskFragmentInfo.java +++ b/core/java/android/window/TaskFragmentInfo.java @@ -23,8 +23,10 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Point; +import android.graphics.Rect; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -66,7 +68,7 @@ public final class TaskFragmentInfo implements Parcelable { private final List<IBinder> mActivities = new ArrayList<>(); /** Relative position of the fragment's top left corner in the parent container. */ - private final Point mPositionInParent; + private final Point mPositionInParent = new Point(); /** * Whether the last running activity in the TaskFragment was finished due to clearing task while @@ -80,21 +82,31 @@ public final class TaskFragmentInfo implements Parcelable { */ private final boolean mIsTaskFragmentClearedForPip; + /** + * The maximum {@link ActivityInfo.WindowLayout#minWidth} and + * {@link ActivityInfo.WindowLayout#minHeight} aggregated from the TaskFragment's child + * activities. + */ + @NonNull + private final Point mMinimumDimensions = new Point(); + /** @hide */ public TaskFragmentInfo( @NonNull IBinder fragmentToken, @NonNull WindowContainerToken token, @NonNull Configuration configuration, int runningActivityCount, boolean isVisible, @NonNull List<IBinder> activities, @NonNull Point positionInParent, - boolean isTaskClearedForReuse, boolean isTaskFragmentClearedForPip) { + boolean isTaskClearedForReuse, boolean isTaskFragmentClearedForPip, + @NonNull Point minimumDimensions) { mFragmentToken = requireNonNull(fragmentToken); mToken = requireNonNull(token); mConfiguration.setTo(configuration); mRunningActivityCount = runningActivityCount; mIsVisible = isVisible; mActivities.addAll(activities); - mPositionInParent = requireNonNull(positionInParent); + mPositionInParent.set(positionInParent); mIsTaskClearedForReuse = isTaskClearedForReuse; mIsTaskFragmentClearedForPip = isTaskFragmentClearedForPip; + mMinimumDimensions.set(minimumDimensions); } @NonNull @@ -154,6 +166,26 @@ public final class TaskFragmentInfo implements Parcelable { } /** + * Returns the minimum width this TaskFragment can be resized to. + * Client side must not {@link WindowContainerTransaction#setBounds(WindowContainerToken, Rect)} + * that {@link Rect#width()} is shorter than the reported value. + * @hide pending unhide + */ + public int getMinimumWidth() { + return mMinimumDimensions.x; + } + + /** + * Returns the minimum width this TaskFragment can be resized to. + * Client side must not {@link WindowContainerTransaction#setBounds(WindowContainerToken, Rect)} + * that {@link Rect#height()} is shorter than the reported value. + * @hide pending unhide + */ + public int getMinimumHeight() { + return mMinimumDimensions.y; + } + + /** * Returns {@code true} if the parameters that are important for task fragment organizers are * equal between this {@link TaskFragmentInfo} and {@param that}. */ @@ -170,7 +202,8 @@ public final class TaskFragmentInfo implements Parcelable { && mActivities.equals(that.mActivities) && mPositionInParent.equals(that.mPositionInParent) && mIsTaskClearedForReuse == that.mIsTaskClearedForReuse - && mIsTaskFragmentClearedForPip == that.mIsTaskFragmentClearedForPip; + && mIsTaskFragmentClearedForPip == that.mIsTaskFragmentClearedForPip + && mMinimumDimensions.equals(that.mMinimumDimensions); } private TaskFragmentInfo(Parcel in) { @@ -180,9 +213,10 @@ public final class TaskFragmentInfo implements Parcelable { mRunningActivityCount = in.readInt(); mIsVisible = in.readBoolean(); in.readBinderList(mActivities); - mPositionInParent = requireNonNull(in.readTypedObject(Point.CREATOR)); + mPositionInParent.readFromParcel(in); mIsTaskClearedForReuse = in.readBoolean(); mIsTaskFragmentClearedForPip = in.readBoolean(); + mMinimumDimensions.readFromParcel(in); } /** @hide */ @@ -194,9 +228,10 @@ public final class TaskFragmentInfo implements Parcelable { dest.writeInt(mRunningActivityCount); dest.writeBoolean(mIsVisible); dest.writeBinderList(mActivities); - dest.writeTypedObject(mPositionInParent, flags); + mPositionInParent.writeToParcel(dest, flags); dest.writeBoolean(mIsTaskClearedForReuse); dest.writeBoolean(mIsTaskFragmentClearedForPip); + mMinimumDimensions.writeToParcel(dest, flags); } @NonNull @@ -224,6 +259,7 @@ public final class TaskFragmentInfo implements Parcelable { + " positionInParent=" + mPositionInParent + " isTaskClearedForReuse=" + mIsTaskClearedForReuse + " isTaskFragmentClearedForPip" + mIsTaskFragmentClearedForPip + + " minimumDimensions" + mMinimumDimensions + "}"; } diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index 51da61ffb9c9..c81184fb2383 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -107,8 +107,11 @@ public final class TransitionInfo implements Parcelable { */ public static final int FLAG_DISPLAY_HAS_ALERT_WINDOWS = 1 << 7; + /** The container is an input-method window. */ + public static final int FLAG_IS_INPUT_METHOD = 1 << 8; + /** The first unused bit. This can be used by remotes to attach custom flags to this change. */ - public static final int FLAG_FIRST_CUSTOM = 1 << 8; + public static final int FLAG_FIRST_CUSTOM = 1 << 9; /** @hide */ @IntDef(prefix = { "FLAG_" }, value = { @@ -121,6 +124,7 @@ public final class TransitionInfo implements Parcelable { FLAG_IS_DISPLAY, FLAG_OCCLUDES_KEYGUARD, FLAG_DISPLAY_HAS_ALERT_WINDOWS, + FLAG_IS_INPUT_METHOD, FLAG_FIRST_CUSTOM }) public @interface ChangeFlags {} @@ -300,6 +304,9 @@ public final class TransitionInfo implements Parcelable { if ((flags & FLAG_IS_WALLPAPER) != 0) { sb.append("IS_WALLPAPER"); } + if ((flags & FLAG_IS_INPUT_METHOD) != 0) { + sb.append("IS_INPUT_METHOD"); + } if ((flags & FLAG_TRANSLUCENT) != 0) { sb.append((sb.length() == 0 ? "" : "|") + "TRANSLUCENT"); } diff --git a/core/java/android/window/TransitionRequestInfo.java b/core/java/android/window/TransitionRequestInfo.java index 48211a8234ee..14046945ede0 100644 --- a/core/java/android/window/TransitionRequestInfo.java +++ b/core/java/android/window/TransitionRequestInfo.java @@ -16,6 +16,8 @@ package android.window; +import static android.view.WindowManager.transitTypeToString; + import android.annotation.Nullable; import android.app.ActivityManager; import android.app.WindowConfiguration; @@ -366,7 +368,7 @@ public final class TransitionRequestInfo implements Parcelable { // String fieldNameToString() { ... } return "TransitionRequestInfo { " + - "type = " + mType + ", " + + "type = " + transitTypeToString(mType) + ", " + "triggerTask = " + mTriggerTask + ", " + "remoteTransition = " + mRemoteTransition + ", " + "displayChange = " + mDisplayChange + diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 7dc039d44f95..633d87937049 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -384,12 +384,10 @@ public final class WindowContainerTransaction implements Parcelable { */ @NonNull public WindowContainerTransaction setAdjacentRoots( - @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2, - boolean moveTogether) { + @NonNull WindowContainerToken root1, @NonNull WindowContainerToken root2) { mHierarchyOps.add(HierarchyOp.createForAdjacentRoots( root1.asBinder(), - root2.asBinder(), - moveTogether)); + root2.asBinder())); return this; } @@ -1106,9 +1104,6 @@ public final class WindowContainerTransaction implements Parcelable { private boolean mReparentTopOnly; - // TODO(b/207185041): Remove this once having a single-top root for split screen. - private boolean mMoveAdjacentTogether; - @Nullable private int[] mWindowingModes; @@ -1171,12 +1166,10 @@ public final class WindowContainerTransaction implements Parcelable { } /** Create a hierarchy op for setting adjacent root tasks. */ - public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2, - boolean moveTogether) { + public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS) .setContainer(root1) .setReparentContainer(root2) - .setMoveAdjacentTogether(moveTogether) .build(); } @@ -1223,7 +1216,6 @@ public final class WindowContainerTransaction implements Parcelable { mInsetsProviderFrame = copy.mInsetsProviderFrame; mToTop = copy.mToTop; mReparentTopOnly = copy.mReparentTopOnly; - mMoveAdjacentTogether = copy.mMoveAdjacentTogether; mWindowingModes = copy.mWindowingModes; mActivityTypes = copy.mActivityTypes; mLaunchOptions = copy.mLaunchOptions; @@ -1245,7 +1237,6 @@ public final class WindowContainerTransaction implements Parcelable { } mToTop = in.readBoolean(); mReparentTopOnly = in.readBoolean(); - mMoveAdjacentTogether = in.readBoolean(); mWindowingModes = in.createIntArray(); mActivityTypes = in.createIntArray(); mLaunchOptions = in.readBundle(); @@ -1300,10 +1291,6 @@ public final class WindowContainerTransaction implements Parcelable { return mReparentTopOnly; } - public boolean getMoveAdjacentTogether() { - return mMoveAdjacentTogether; - } - public int[] getWindowingModes() { return mWindowingModes; } @@ -1356,8 +1343,7 @@ public final class WindowContainerTransaction implements Parcelable { return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}"; case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "{SetAdjacentRoot: container=" + mContainer - + " adjacentRoot=" + mReparent + " mMoveAdjacentTogether=" - + mMoveAdjacentTogether + "}"; + + " adjacentRoot=" + mReparent + "}"; case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "{LaunchTask: " + mLaunchOptions + "}"; case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: @@ -1413,7 +1399,6 @@ public final class WindowContainerTransaction implements Parcelable { } dest.writeBoolean(mToTop); dest.writeBoolean(mReparentTopOnly); - dest.writeBoolean(mMoveAdjacentTogether); dest.writeIntArray(mWindowingModes); dest.writeIntArray(mActivityTypes); dest.writeBundle(mLaunchOptions); @@ -1458,8 +1443,6 @@ public final class WindowContainerTransaction implements Parcelable { private boolean mReparentTopOnly; - private boolean mMoveAdjacentTogether; - @Nullable private int[] mWindowingModes; @@ -1515,11 +1498,6 @@ public final class WindowContainerTransaction implements Parcelable { return this; } - Builder setMoveAdjacentTogether(boolean moveAdjacentTogether) { - mMoveAdjacentTogether = moveAdjacentTogether; - return this; - } - Builder setWindowingModes(@Nullable int[] windowingModes) { mWindowingModes = windowingModes; return this; @@ -1570,7 +1548,6 @@ public final class WindowContainerTransaction implements Parcelable { hierarchyOp.mInsetsProviderFrame = mInsetsProviderFrame; hierarchyOp.mToTop = mToTop; hierarchyOp.mReparentTopOnly = mReparentTopOnly; - hierarchyOp.mMoveAdjacentTogether = mMoveAdjacentTogether; hierarchyOp.mLaunchOptions = mLaunchOptions; hierarchyOp.mActivityIntent = mActivityIntent; hierarchyOp.mPendingIntent = mPendingIntent; diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index f727d80bba12..e258fc19a75f 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -914,6 +914,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + if (!st.hasPanelItems()) { + // Ensure that |st.decorView| has its actual content. Otherwise, an empty window can be + // created and cause ANR. + return; + } + st.isHandled = false; WindowManager.LayoutParams lp = new WindowManager.LayoutParams( diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java index 377dfd0fb137..295dc545ef4b 100644 --- a/core/java/com/android/internal/policy/TransitionAnimation.java +++ b/core/java/com/android/internal/policy/TransitionAnimation.java @@ -25,6 +25,7 @@ import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_NONE; import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_UNSET; import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_WALLPAPER_INTRA_OPEN; import static android.view.WindowManager.TRANSIT_OPEN; @@ -258,11 +259,17 @@ public class TransitionAnimation { } return null; } - - /** Load animation by attribute Id from a specific AnimationStyle resource. */ + /** + * Load animation by attribute Id from a specific AnimationStyle resource. + * + * @param translucent {@code true} if we're sure that the animation is applied on a translucent + * window container, {@code false} otherwise. + * @param transit {@link TransitionOldType} for the app transition of this animation, or + * {@link TransitionOldType#TRANSIT_OLD_UNSET} if app transition type is unknown. + */ @Nullable - public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr, - boolean translucent) { + private Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr, + boolean translucent, @TransitionOldType int transit) { if (animStyleResId == 0) { return null; } @@ -278,6 +285,8 @@ public class TransitionAnimation { } if (translucent) { resId = updateToTranslucentAnimIfNeeded(resId); + } else if (transit != TRANSIT_OLD_UNSET) { + resId = updateToTranslucentAnimIfNeeded(resId, transit); } if (ResourceId.isValid(resId)) { return loadAnimationSafely(context, resId, mTag); @@ -285,6 +294,15 @@ public class TransitionAnimation { return null; } + + /** Load animation by attribute Id from a specific AnimationStyle resource. */ + @Nullable + public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr, + boolean translucent) { + return loadAnimationAttr(packageName, animStyleResId, animAttr, translucent, + TRANSIT_OLD_UNSET); + } + /** Load animation by attribute Id from android package. */ @Nullable public Animation loadDefaultAnimationAttr(int animAttr, boolean translucent) { @@ -292,6 +310,13 @@ public class TransitionAnimation { translucent); } + /** Load animation by attribute Id from android package. */ + @Nullable + public Animation loadDefaultAnimationAttr(int animAttr, @TransitionOldType int transit) { + return loadAnimationAttr(DEFAULT_PACKAGE, mDefaultWindowAnimationStyleResId, animAttr, + false /* translucent */, transit); + } + @Nullable private AttributeCache.Entry getCachedAnimations(LayoutParams lp) { if (mDebug) { diff --git a/core/proto/android/server/windowmanagertransitiontrace.proto b/core/proto/android/server/windowmanagertransitiontrace.proto new file mode 100644 index 000000000000..9429127b2f6e --- /dev/null +++ b/core/proto/android/server/windowmanagertransitiontrace.proto @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2022 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. + */ + +syntax = "proto3"; + +package com.android.server.wm.shell; + +import "frameworks/base/core/proto/android/server/windowmanagerservice.proto"; + +option java_multiple_files = true; + +/* Represents a file full of transition entries. + Encoded, it should start with 0x9 0x57 0x49 0x4e 0x54 0x52 0x41 0x43 0x45 (.TRNTRACE), such + that it can be easily identified. */ +message TransitionTraceProto { + + /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L + (this is needed because enums have to be 32 bits and there's no nice way to put 64bit + constants into .proto files. */ + enum MagicNumber { + INVALID = 0; + MAGIC_NUMBER_L = 0x544e5254; /* TRNT (little-endian ASCII) */ + MAGIC_NUMBER_H = 0x45434152; /* RACE (little-endian ASCII) */ + } + + fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ + int64 timestamp = 2; /* The timestamp of when the trace was started. */ + repeated Transition transition = 3; +} + +message Transition { + + enum State { + COLLECTING = 0; + PENDING = -1; + STARTED = 1; + PLAYING = 2; + ABORT = 3; + FINISHED = 4; + } + + int32 id = 1; + int32 transition_type = 2; + int64 timestamp = 3; + State state = 5; + int32 flags = 6; + repeated ChangeInfo change = 7; +} + +message ChangeInfo { + com.android.server.wm.IdentifierProto window_identifier = 1; + int32 transit_mode = 2; + bool has_changed = 3; + int32 change_flags = 4; +} diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d2f31b05313d..515ea5006667 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2422,6 +2422,15 @@ <!-- When closing the current activity, this is the animation that is run on the current activity (which is exiting the screen). --> <attr name="activityCloseExitAnimation" format="reference" /> + <!-- When closing a dream activity, this is the animation that is + run on the dream activity (which is exiting the screen). --> + <attr name="dreamActivityCloseExitAnimation" format="reference" /> + <!-- When opening a dream activity, this is the animation that is + run on the dream activity (which is entering the screen). --> + <attr name="dreamActivityOpenEnterAnimation" format="reference" /> + <!-- When opening a dream activity, this is the animation that is + run on the old activity (which is exiting the screen). --> + <attr name="dreamActivityOpenExitAnimation" format="reference" /> <!-- When opening an activity in a new task, this is the animation that is run on the activity of the new task (which is entering the screen). --> <attr name="taskOpenEnterAnimation" format="reference" /> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 54c3490740e5..46abd3345335 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3755,6 +3755,9 @@ "Guest" and "Reset guest". --> <bool name="config_guestUserAutoCreated">false</bool> + <!-- If true, owner can change guest user ephemeral state via UI option --> + <bool name="config_guestUserAllowEphemeralStateChange">true</bool> + <!-- Enforce strong auth on boot. Setting this to false represents a security risk and should not be ordinarily done. The only case in which this might be permissible is in a car head unit where there are hardware mechanisms to protect the device (physical keys) and not @@ -5133,16 +5136,24 @@ --> <color name="config_letterboxBackgroundColor">@android:color/system_neutral2_900</color> - <!-- Horizonal position of a center of the letterboxed app window. + <!-- Horizontal position of a center of the letterboxed app window. 0 corresponds to the left side of the screen and 1 to the right side. If given value < 0 - or > 1, it is ignored and central positionis used (0.5). --> + or > 1, it is ignored and central position is used (0.5). --> <item name="config_letterboxHorizontalPositionMultiplier" format="float" type="dimen">0.5</item> - <!-- Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape - device orientation. --> - <bool name="config_letterboxIsReachabilityEnabled">false</bool> + <!-- Vertical position of a center of the letterboxed app window. + 0 corresponds to the upper side of the screen and 1 to the lower side. If given value < 0 + or > 1, it is ignored and central position is used (0.5). --> + <item name="config_letterboxVerticalPositionMultiplier" format="float" type="dimen">0.5</item> + + <!-- Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps. + --> + <bool name="config_letterboxIsHorizontalReachabilityEnabled">false</bool> + + <!-- Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps. --> + <bool name="config_letterboxIsVerticalReachabilityEnabled">false</bool> - <!-- Default horizonal position of the letterboxed app window when reachability is + <!-- Default horizontal position of the letterboxed app window when reachability is enabled and an app is fullscreen in landscape device orientation. When reachability is enabled, the position can change between left, center and right. This config defines the default one: @@ -5150,11 +5161,30 @@ - Option 1 - Center. - Option 2 - Right. If given value is outside of this range, the option 1 (center) is assummed. --> - <integer name="config_letterboxDefaultPositionForReachability">1</integer> + <integer name="config_letterboxDefaultPositionForHorizontalReachability">1</integer> + + <!-- Default vertical position of the letterboxed app window when reachability is + enabled and an app is fullscreen in portrait device orientation. When reachability is + enabled, the position can change between top, center and bottom. This config defines the + default one: + - Option 0 - Top. + - Option 1 - Center. + - Option 2 - Bottom. + If given value is outside of this range, the option 1 (center) is assummed. --> + <integer name="config_letterboxDefaultPositionForVerticalReachability">1</integer> <!-- Whether displaying letterbox education is enabled for letterboxed fullscreen apps. --> <bool name="config_letterboxIsEducationEnabled">false</bool> + <!-- Default min aspect ratio for unresizable apps which is used when an app doesn't specify + android:minAspectRatio in accordance with CDD 7.1.1.2 requirement: + https://source.android.com/compatibility/12/android-12-cdd#7112_screen_aspect_ratio. + An exception will be thrown if the given aspect ratio < 4:3. --> + <item name="config_letterboxDefaultMinAspectRatioForUnresizableApps" format="float" type="dimen">1.5</item> + + <!-- Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. --> + <bool name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled">false</bool> + <!-- Whether a camera compat controller is enabled to allow the user to apply or revert treatment for stretched issues in camera viewfinder. --> <bool name="config_isCameraCompatControlForStretchedIssuesEnabled">false</bool> @@ -5813,4 +5843,7 @@ <string-array name="config_serviceStateLocationAllowedPackages"> <item>"com.android.phone"</item> </string-array> + + <!-- Whether the wake screen on notifications feature is available. --> + <bool name="config_pulseOnNotificationsAvailable">true</bool> </resources> diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 5d17047d7430..bad05e077b7d 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -85,6 +85,9 @@ please see styles_device_defaults.xml. <item name="activityOpenExitAnimation">@anim/activity_open_exit</item> <item name="activityCloseEnterAnimation">@anim/activity_close_enter</item> <item name="activityCloseExitAnimation">@anim/activity_close_exit</item> + <item name="dreamActivityCloseExitAnimation">@anim/dream_activity_close_exit</item> + <item name="dreamActivityOpenEnterAnimation">@anim/dream_activity_open_enter</item> + <item name="dreamActivityOpenExitAnimation">@anim/dream_activity_open_exit</item> <item name="taskOpenEnterAnimation">@anim/task_open_enter</item> <item name="taskOpenExitAnimation">@anim/task_open_exit</item> <item name="launchTaskBehindTargetAnimation">@anim/launch_task_behind_target</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 8f6056273ca4..ad02128db613 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -394,6 +394,7 @@ <java-symbol type="bool" name="config_supportsInsecureLockScreen" /> <java-symbol type="bool" name="config_guestUserEphemeral" /> <java-symbol type="bool" name="config_guestUserAutoCreated" /> + <java-symbol type="bool" name="config_guestUserAllowEphemeralStateChange" /> <java-symbol type="bool" name="config_localDisplaysMirrorContent" /> <java-symbol type="array" name="config_localPrivateDisplayPorts" /> <java-symbol type="integer" name="config_defaultDisplayDefaultColorMode" /> @@ -3336,6 +3337,7 @@ <java-symbol type="string" name="config_dozeTapSensorType" /> <java-symbol type="array" name="config_dozeTapSensorPostureMapping" /> <java-symbol type="bool" name="config_dozePulsePickup" /> + <java-symbol type="bool" name="config_pulseOnNotificationsAvailable" /> <!-- Used for MimeIconUtils. --> <java-symbol type="drawable" name="ic_doc_apk" /> @@ -4387,9 +4389,14 @@ <java-symbol type="integer" name="config_letterboxBackgroundType" /> <java-symbol type="color" name="config_letterboxBackgroundColor" /> <java-symbol type="dimen" name="config_letterboxHorizontalPositionMultiplier" /> - <java-symbol type="bool" name="config_letterboxIsReachabilityEnabled" /> - <java-symbol type="integer" name="config_letterboxDefaultPositionForReachability" /> + <java-symbol type="dimen" name="config_letterboxVerticalPositionMultiplier" /> + <java-symbol type="bool" name="config_letterboxIsHorizontalReachabilityEnabled" /> + <java-symbol type="bool" name="config_letterboxIsVerticalReachabilityEnabled" /> + <java-symbol type="integer" name="config_letterboxDefaultPositionForHorizontalReachability" /> + <java-symbol type="integer" name="config_letterboxDefaultPositionForVerticalReachability" /> <java-symbol type="bool" name="config_letterboxIsEducationEnabled" /> + <java-symbol type="dimen" name="config_letterboxDefaultMinAspectRatioForUnresizableApps" /> + <java-symbol type="bool" name="config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled" /> <java-symbol type="bool" name="config_isCameraCompatControlForStretchedIssuesEnabled" /> <java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" /> diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index bfb2fd57975f..a2d4bafef41c 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -31,7 +31,6 @@ import static org.junit.Assert.assertTrue; import android.annotation.Nullable; import android.app.Activity; -import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ActivityThread.ActivityClientRecord; import android.app.IApplicationThread; @@ -571,53 +570,6 @@ public class ActivityThreadTest { } @Test - public void testHandleProcessConfigurationChanged_DependOnProcessState() { - final ActivityThread activityThread = ActivityThread.currentActivityThread(); - final Configuration origConfig = activityThread.getConfiguration(); - final int newDpi = origConfig.densityDpi + 10; - final Configuration newConfig = new Configuration(origConfig); - newConfig.seq++; - newConfig.densityDpi = newDpi; - - activityThread.updateProcessState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, - false /* fromIPC */); - - applyProcessConfiguration(activityThread, newConfig); - try { - // In the cached state, the configuration is only set as pending and not applied. - assertEquals(origConfig.densityDpi, activityThread.getConfiguration().densityDpi); - assertTrue(activityThread.isCachedProcessState()); - } finally { - // The foreground state is the default state of instrumentation. - activityThread.updateProcessState(ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, - false /* fromIPC */); - } - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - - try { - // The state becomes non-cached, the pending configuration should be applied. - assertEquals(newConfig.densityDpi, activityThread.getConfiguration().densityDpi); - assertFalse(activityThread.isCachedProcessState()); - } finally { - // Restore to the original configuration. - activityThread.getConfiguration().seq = origConfig.seq - 1; - applyProcessConfiguration(activityThread, origConfig); - } - } - - private static void applyProcessConfiguration(ActivityThread thread, Configuration config) { - final ClientTransaction clientTransaction = newTransaction(thread, - null /* activityToken */); - clientTransaction.addCallback(ConfigurationChangeItem.obtain(config)); - final IApplicationThread appThread = thread.getApplicationThread(); - try { - appThread.scheduleTransaction(clientTransaction); - } catch (Exception ignored) { - } - InstrumentationRegistry.getInstrumentation().waitForIdleSync(); - } - - @Test public void testResumeAfterNewIntent() { final Activity activity = mActivityTestRule.launchActivity(new Intent()); final ActivityThread activityThread = activity.getActivityThread(); diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index f7c2b732a77b..f8da95d04411 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -163,6 +163,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1941440781": { + "message": "Creating Pending Move-to-back: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/Task.java" + }, "-1939861963": { "message": "Create root task displayId=%d winMode=%d", "level": "VERBOSE", @@ -469,6 +475,12 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/ResetTargetTaskHelper.java" }, + "-1635750891": { + "message": "Received remote change for Display[%d], applied: [%dx%d, rot = %d]", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/RemoteDisplayChangeController.java" + }, "-1633115609": { "message": "Key dispatch not paused for screen off", "level": "VERBOSE", @@ -757,6 +769,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "-1383884640": { + "message": " allReady query: used=%b override=%b defer=%d states=[%s]", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/Transition.java" + }, "-1376035390": { "message": "No task found", "level": "DEBUG", @@ -2071,6 +2089,12 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "-57572004": { + "message": "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b canCustomizeAppTransition=%b Callers=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", + "at": "com\/android\/server\/wm\/AppTransition.java" + }, "-55185509": { "message": "setFocusedTask: taskId=%d touchedActivity=%s", "level": "DEBUG", @@ -2143,6 +2167,12 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimationController.java" }, + "25888308": { + "message": "Resize reasons for w=%s: %s configChanged=%b dragResizingChanged=%b", + "level": "VERBOSE", + "group": "WM_DEBUG_RESIZE", + "at": "com\/android\/server\/wm\/WindowState.java" + }, "35398067": { "message": "goodToGo(): onAnimationStart, transit=%s, apps=%d, wallpapers=%d, nonApps=%d", "level": "DEBUG", @@ -2527,12 +2557,6 @@ "group": "WM_DEBUG_ADD_REMOVE", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "352982444": { - "message": " allReady query: used=%b override=%b states=[%s]", - "level": "VERBOSE", - "group": "WM_DEBUG_WINDOW_TRANSITIONS", - "at": "com\/android\/server\/wm\/Transition.java" - }, "355720268": { "message": "stopFreezingDisplayLocked: Unfreezing now", "level": "DEBUG", @@ -2797,12 +2821,6 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/DisplayContent.java" }, - "625447638": { - "message": "Resize reasons for w=%s: %s configChanged=%b dragResizingChanged=%b reportOrientationChanged=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_RESIZE", - "at": "com\/android\/server\/wm\/WindowState.java" - }, "628276090": { "message": "Delaying app transition for screen rotation animation to finish", "level": "VERBOSE", @@ -3049,6 +3067,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, + "898260097": { + "message": "Creating Pending Pip-Enter: %s", + "level": "VERBOSE", + "group": "WM_DEBUG_WINDOW_TRANSITIONS", + "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" + }, "898863925": { "message": "Attempted to add QS dialog window with unknown token %s. Aborting.", "level": "WARN", @@ -3379,12 +3403,6 @@ "group": "WM_DEBUG_BOOT", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "1246035185": { - "message": "stopFreezingDisplayLocked: Returning waitingForConfig=%b, waitingForRemoteRotation=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d", - "level": "DEBUG", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/WindowManagerService.java" - }, "1252594551": { "message": "Window types in WindowContext and LayoutParams.type should match! Type from LayoutParams is %d, but type from WindowContext is %d", "level": "WARN", @@ -3481,6 +3499,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" }, + "1360176455": { + "message": "stopFreezingDisplayLocked: Returning waitingForConfig=%b, waitingForRemoteDisplayChange=%b, mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, mClientFreezingScreen=%b, mOpeningApps.size()=%d", + "level": "DEBUG", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/WindowManagerService.java" + }, "1364126018": { "message": "Resumed activity; dropping state of: %s", "level": "INFO", @@ -3505,6 +3529,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/TaskDisplayArea.java" }, + "1393721079": { + "message": "Starting remote display change: from [rot = %d], to [%dx%d, rot = %d]", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/RemoteDisplayChangeController.java" + }, "1396893178": { "message": "createRootTask unknown displayId=%d", "level": "ERROR", @@ -3847,6 +3877,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowStateAnimator.java" }, + "1764619787": { + "message": "Remote change for Display[%d]: timeout reached", + "level": "VERBOSE", + "group": "WM_DEBUG_CONFIGURATION", + "at": "com\/android\/server\/wm\/RemoteDisplayChangeController.java" + }, "1774661765": { "message": "Devices still not ready after waiting %d milliseconds before attempting to detect safe mode.", "level": "WARN", @@ -4171,6 +4207,12 @@ "group": "WM_DEBUG_ANIM", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "2079410261": { + "message": "applyAnimation: override requested, but it is prohibited by policy.", + "level": "ERROR", + "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", + "at": "com\/android\/server\/wm\/AppTransition.java" + }, "2083556954": { "message": "Set mOrientationChanging of %s", "level": "VERBOSE", @@ -4224,12 +4266,6 @@ "level": "VERBOSE", "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/DisplayRotation.java" - }, - "2137411379": { - "message": "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b Callers=%s", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", - "at": "com\/android\/server\/wm\/AppTransition.java" } }, "groups": { diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java index 25f76f6f6da7..2781ac4bf1da 100644 --- a/graphics/java/android/graphics/Point.java +++ b/graphics/java/android/graphics/Point.java @@ -36,8 +36,7 @@ public class Point implements Parcelable { } public Point(@NonNull Point src) { - this.x = src.x; - this.y = src.y; + set(src); } /** @@ -49,6 +48,15 @@ public class Point implements Parcelable { } /** + * Sets the point's from {@code src}'s coordinates + * @hide + */ + public void set(@NonNull Point src) { + this.x = src.x; + this.y = src.y; + } + + /** * Negate the point's coordinates */ public final void negate() { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java index 921552b6cfbb..68ff806c6765 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java @@ -199,7 +199,7 @@ public final class CommonFoldingFeature { throw new IllegalArgumentException( "Display feature rectangle cannot have zero width and height simultaneously."); } - this.mRect = rect; + this.mRect = new Rect(rect); } /** Returns the type of the feature. */ @@ -217,7 +217,7 @@ public final class CommonFoldingFeature { /** Returns the bounds of the feature. */ @NonNull public Rect getRect() { - return mRect; + return new Rect(mRect); } @Override diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java index fdcb7be597d5..cc2bb63ca8e1 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java @@ -22,7 +22,6 @@ import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN; import static androidx.window.common.CommonFoldingFeature.parseListFromString; import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.Context; import android.hardware.devicestate.DeviceStateManager; import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; @@ -30,22 +29,25 @@ import android.text.TextUtils; import android.util.Log; import android.util.SparseIntArray; +import androidx.window.util.AcceptOnceConsumer; import androidx.window.util.BaseDataProducer; -import androidx.window.util.DataProducer; import com.android.internal.R; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; /** - * An implementation of {@link androidx.window.util.DataProducer} that returns the device's posture - * by mapping the state returned from {@link DeviceStateManager} to values provided in the resources - * config at {@link R.array#config_device_state_postures}. + * An implementation of {@link androidx.window.util.BaseDataProducer} that returns + * the device's posture by mapping the state returned from {@link DeviceStateManager} to + * values provided in the resources' config at {@link R.array#config_device_state_postures}. */ -public final class DeviceStateManagerFoldingFeatureProducer extends - BaseDataProducer<List<CommonFoldingFeature>> { +public final class DeviceStateManagerFoldingFeatureProducer + extends BaseDataProducer<List<CommonFoldingFeature>> { private static final String TAG = DeviceStateManagerFoldingFeatureProducer.class.getSimpleName(); private static final boolean DEBUG = false; @@ -54,15 +56,11 @@ public final class DeviceStateManagerFoldingFeatureProducer extends private int mCurrentDeviceState = INVALID_DEVICE_STATE; - private final DeviceStateCallback mDeviceStateCallback = (state) -> { - mCurrentDeviceState = state; - notifyDataChanged(); - }; @NonNull - private final DataProducer<String> mRawFoldSupplier; + private final BaseDataProducer<String> mRawFoldSupplier; public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context, - @NonNull DataProducer<String> rawFoldSupplier) { + @NonNull BaseDataProducer<String> rawFoldSupplier) { mRawFoldSupplier = rawFoldSupplier; String[] deviceStatePosturePairs = context.getResources() .getStringArray(R.array.config_device_state_postures); @@ -70,7 +68,8 @@ public final class DeviceStateManagerFoldingFeatureProducer extends String[] deviceStatePostureMapping = deviceStatePosturePair.split(":"); if (deviceStatePostureMapping.length != 2) { if (DEBUG) { - Log.e(TAG, "Malformed device state posture pair: " + deviceStatePosturePair); + Log.e(TAG, "Malformed device state posture pair: " + + deviceStatePosturePair); } continue; } @@ -82,7 +81,8 @@ public final class DeviceStateManagerFoldingFeatureProducer extends posture = Integer.parseInt(deviceStatePostureMapping[1]); } catch (NumberFormatException e) { if (DEBUG) { - Log.e(TAG, "Failed to parse device state or posture: " + deviceStatePosturePair, + Log.e(TAG, "Failed to parse device state or posture: " + + deviceStatePosturePair, e); } continue; @@ -92,32 +92,95 @@ public final class DeviceStateManagerFoldingFeatureProducer extends } if (mDeviceStateToPostureMap.size() > 0) { - context.getSystemService(DeviceStateManager.class) - .registerCallback(context.getMainExecutor(), mDeviceStateCallback); + DeviceStateCallback deviceStateCallback = (state) -> { + mCurrentDeviceState = state; + mRawFoldSupplier.getData(this::notifyFoldingFeatureChange); + }; + Objects.requireNonNull(context.getSystemService(DeviceStateManager.class)) + .registerCallback(context.getMainExecutor(), deviceStateCallback); } } - @Override - @Nullable - public Optional<List<CommonFoldingFeature>> getData() { - final int globalHingeState = globalHingeState(); - Optional<String> displayFeaturesString = mRawFoldSupplier.getData(); - if (displayFeaturesString.isEmpty() || TextUtils.isEmpty(displayFeaturesString.get())) { - return Optional.empty(); + /** + * Add a callback to mCallbacks if there is no device state. This callback will be run + * once a device state is set. Otherwise,run the callback immediately. + */ + private void runCallbackWhenValidState(@NonNull Consumer<List<CommonFoldingFeature>> callback, + String displayFeaturesString) { + if (isCurrentStateValid()) { + callback.accept(calculateFoldingFeature(displayFeaturesString)); + } else { + // This callback will be added to mCallbacks and removed once it runs once. + AcceptOnceConsumer<List<CommonFoldingFeature>> singleRunCallback = + new AcceptOnceConsumer<>(this, callback); + addDataChangedCallback(singleRunCallback); } - return Optional.of(parseListFromString(displayFeaturesString.get(), globalHingeState)); + } + + /** + * Checks to find {@link DeviceStateManagerFoldingFeatureProducer#mCurrentDeviceState} in the + * {@link DeviceStateManagerFoldingFeatureProducer#mDeviceStateToPostureMap} which was + * initialized in the constructor of {@link DeviceStateManagerFoldingFeatureProducer}. + * Returns a boolean value of whether the device state is valid. + */ + private boolean isCurrentStateValid() { + // If the device state is not found in the map, indexOfKey returns a negative number. + return mDeviceStateToPostureMap.indexOfKey(mCurrentDeviceState) >= 0; } @Override - protected void onListenersChanged(Set<Runnable> callbacks) { + protected void onListenersChanged( + @NonNull Set<Consumer<List<CommonFoldingFeature>>> callbacks) { super.onListenersChanged(callbacks); if (callbacks.isEmpty()) { - mRawFoldSupplier.removeDataChangedCallback(this::notifyDataChanged); + mCurrentDeviceState = INVALID_DEVICE_STATE; + mRawFoldSupplier.removeDataChangedCallback(this::notifyFoldingFeatureChange); + } else { + mRawFoldSupplier.addDataChangedCallback(this::notifyFoldingFeatureChange); + } + } + + @NonNull + @Override + public Optional<List<CommonFoldingFeature>> getCurrentData() { + Optional<String> displayFeaturesString = mRawFoldSupplier.getCurrentData(); + if (!isCurrentStateValid()) { + return Optional.empty(); + } else { + return displayFeaturesString.map(this::calculateFoldingFeature); + } + } + + /** + * Adds the data to the storeFeaturesConsumer when the data is ready. + * @param storeFeaturesConsumer a consumer to collect the data when it is first available. + */ + public void getData(Consumer<List<CommonFoldingFeature>> storeFeaturesConsumer) { + mRawFoldSupplier.getData((String displayFeaturesString) -> { + if (TextUtils.isEmpty(displayFeaturesString)) { + storeFeaturesConsumer.accept(new ArrayList<>()); + } else { + runCallbackWhenValidState(storeFeaturesConsumer, displayFeaturesString); + } + }); + } + + private void notifyFoldingFeatureChange(String displayFeaturesString) { + if (!isCurrentStateValid()) { + return; + } + if (TextUtils.isEmpty(displayFeaturesString)) { + notifyDataChanged(new ArrayList<>()); } else { - mRawFoldSupplier.addDataChangedCallback(this::notifyDataChanged); + notifyDataChanged(calculateFoldingFeature(displayFeaturesString)); } } + private List<CommonFoldingFeature> calculateFoldingFeature(String displayFeaturesString) { + final int globalHingeState = globalHingeState(); + return parseListFromString(displayFeaturesString, globalHingeState); + } + private int globalHingeState() { return mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java index 69ad1badce60..7906342d445d 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java @@ -32,6 +32,7 @@ import com.android.internal.R; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; /** * Implementation of {@link androidx.window.util.DataProducer} that produces a @@ -40,7 +41,7 @@ import java.util.Set; * settings where the {@link String} property is saved with the key * {@link RawFoldingFeatureProducer#DISPLAY_FEATURES}. If this value is null or empty then the * value in {@link android.content.res.Resources} is used. If both are empty then - * {@link RawFoldingFeatureProducer#getData()} returns an empty object. + * {@link RawFoldingFeatureProducer#getData} returns an empty object. * {@link RawFoldingFeatureProducer} listens to changes in the setting so that it can override * the system {@link CommonFoldingFeature} data. */ @@ -63,12 +64,13 @@ public final class RawFoldingFeatureProducer extends BaseDataProducer<String> { @Override @NonNull - public Optional<String> getData() { + public void getData(Consumer<String> dataConsumer) { String displayFeaturesString = getFeatureString(); if (displayFeaturesString == null) { - return Optional.empty(); + dataConsumer.accept(""); + } else { + dataConsumer.accept(displayFeaturesString); } - return Optional.of(displayFeaturesString); } /** @@ -84,7 +86,7 @@ public final class RawFoldingFeatureProducer extends BaseDataProducer<String> { } @Override - protected void onListenersChanged(Set<Runnable> callbacks) { + protected void onListenersChanged(Set<Consumer<String>> callbacks) { if (callbacks.isEmpty()) { unregisterObserversIfNeeded(); } else { @@ -92,6 +94,12 @@ public final class RawFoldingFeatureProducer extends BaseDataProducer<String> { } } + @NonNull + @Override + public Optional<String> getCurrentData() { + return Optional.of(getFeatureString()); + } + /** * Registers settings observers, if needed. When settings observers are registered for this * producer callbacks for changes in data will be triggered. @@ -125,8 +133,8 @@ public final class RawFoldingFeatureProducer extends BaseDataProducer<String> { @Override public void onChange(boolean selfChange, Uri uri) { if (mDisplayFeaturesUri.equals(uri)) { - notifyDataChanged(); + notifyDataChanged(getFeatureString()); } } } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 44af1a9fd780..f09a91018bf0 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -18,6 +18,8 @@ package androidx.window.extensions.embedding; import android.annotation.NonNull; import android.app.Activity; +import android.util.Pair; +import android.util.Size; /** * Client-side descriptor of a split that holds two containers. @@ -66,6 +68,13 @@ class SplitContainer { return mSplitRule; } + /** Returns the minimum dimension pair of primary container and secondary container. */ + @NonNull + Pair<Size, Size> getMinDimensionsPair() { + return new Pair<>(mPrimaryContainer.getMinDimensions(), + mSecondaryContainer.getMinDimensions()); + } + boolean isPlaceholderContainer() { return (mSplitRule instanceof SplitPlaceholderRule); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 575c3f002791..242e9ab6beee 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -24,9 +24,11 @@ import static androidx.window.extensions.embedding.SplitContainer.getFinishSecon import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule; import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenAdjacent; import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAssociatedContainerWhenStacked; +import static androidx.window.extensions.embedding.SplitPresenter.boundsSmallerThanMinDimensions; +import static androidx.window.extensions.embedding.SplitPresenter.getActivityIntentMinDimensionsPair; +import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions; +import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSideBySide; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityClient; import android.app.ActivityOptions; @@ -43,11 +45,15 @@ import android.os.IBinder; import android.os.Looper; import android.util.ArraySet; import android.util.Log; +import android.util.Pair; +import android.util.Size; import android.util.SparseArray; import android.window.TaskFragmentInfo; import android.window.WindowContainerTransaction; import androidx.annotation.GuardedBy; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.window.common.EmptyLifecycleCallbacksAdapter; import com.android.internal.annotations.VisibleForTesting; @@ -63,7 +69,7 @@ import java.util.function.Consumer; */ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmentCallback, ActivityEmbeddingComponent { - private static final String TAG = "SplitController"; + static final String TAG = "SplitController"; @VisibleForTesting @GuardedBy("mLock") @@ -350,7 +356,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (!(rule instanceof SplitRule)) { continue; } - if (mPresenter.shouldShowSideBySide(taskContainer.getTaskBounds(), (SplitRule) rule)) { + if (shouldShowSideBySide(taskContainer.getTaskBounds(), (SplitRule) rule)) { return true; } } @@ -610,11 +616,15 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen primaryActivity); final SplitContainer splitContainer = getActiveSplitForContainer(primaryContainer); if (splitContainer != null && primaryContainer == splitContainer.getPrimaryContainer() - && canReuseContainer(splitRule, splitContainer.getSplitRule())) { + && canReuseContainer(splitRule, splitContainer.getSplitRule()) + && !boundsSmallerThanMinDimensions(primaryContainer.getLastRequestedBounds(), + getMinDimensions(primaryActivity))) { // Can launch in the existing secondary container if the rules share the same // presentation. final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer(); - if (secondaryContainer == getContainerWithActivity(secondaryActivity)) { + if (secondaryContainer == getContainerWithActivity(secondaryActivity) + && !boundsSmallerThanMinDimensions(secondaryContainer.getLastRequestedBounds(), + getMinDimensions(secondaryActivity))) { // The activity is already in the target TaskFragment. return true; } @@ -791,9 +801,15 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen && (canReuseContainer(splitRule, splitContainer.getSplitRule()) // TODO(b/231845476) we should always respect clearTop. || !respectClearTop)) { - // Can launch in the existing secondary container if the rules share the same - // presentation. - return splitContainer.getSecondaryContainer(); + final Rect secondaryBounds = splitContainer.getSecondaryContainer() + .getLastRequestedBounds(); + if (secondaryBounds.isEmpty() + || !boundsSmallerThanMinDimensions(secondaryBounds, + getMinDimensions(intent))) { + // Can launch in the existing secondary container if the rules share the same + // presentation. + return splitContainer.getSecondaryContainer(); + } } // Create a new TaskFragment to split with the primary activity for the new activity. return mPresenter.createNewSplitWithEmptySideContainer(wct, primaryActivity, intent, @@ -1117,8 +1133,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Check if there is enough space for launch final SplitPlaceholderRule placeholderRule = getPlaceholderRule(activity); - if (placeholderRule == null || !mPresenter.shouldShowSideBySide( - mPresenter.getParentContainerBounds(activity), placeholderRule)) { + + if (placeholderRule == null) { + return false; + } + + final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(activity, + placeholderRule.getPlaceholderIntent()); + if (!shouldShowSideBySide( + mPresenter.getParentContainerBounds(activity), placeholderRule, + minDimensionsPair)) { return false; } @@ -1161,7 +1185,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return false; } - if (mPresenter.shouldShowSideBySide(splitContainer)) { + if (shouldShowSideBySide(splitContainer)) { return false; } @@ -1233,7 +1257,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // Splits that are not showing side-by-side are reported as having 0 split // ratio, since by definition in the API the primary container occupies no // width of the split when covered by the secondary. - mPresenter.shouldShowSideBySide(container) + shouldShowSideBySide(container) ? container.getSplitRule().getSplitRatio() : 0.0f); splitStates.add(splitState); @@ -1402,7 +1426,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } // Decide whether the associated container should be retained based on the current // presentation mode. - if (mPresenter.shouldShowSideBySide(splitContainer)) { + if (shouldShowSideBySide(splitContainer)) { return !shouldFinishAssociatedContainerWhenAdjacent(finishBehavior); } else { return !shouldFinishAssociatedContainerWhenStacked(finishBehavior); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index ac3b05a0e825..1b79ad999435 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -16,15 +16,23 @@ package androidx.window.extensions.embedding; +import static android.content.pm.PackageManager.MATCH_ALL; + import android.app.Activity; +import android.app.ActivityThread; import android.app.WindowConfiguration; import android.app.WindowConfiguration.WindowingMode; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; import android.util.LayoutDirection; +import android.util.Pair; +import android.util.Size; import android.view.View; import android.view.WindowInsets; import android.view.WindowMetrics; @@ -34,6 +42,8 @@ import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; + import java.util.concurrent.Executor; /** @@ -41,9 +51,12 @@ import java.util.concurrent.Executor; * {@link SplitController}. */ class SplitPresenter extends JetpackTaskFragmentOrganizer { - private static final int POSITION_START = 0; - private static final int POSITION_END = 1; - private static final int POSITION_FILL = 2; + @VisibleForTesting + static final int POSITION_START = 0; + @VisibleForTesting + static final int POSITION_END = 1; + @VisibleForTesting + static final int POSITION_FILL = 2; @IntDef(value = { POSITION_START, @@ -103,8 +116,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { @NonNull WindowContainerTransaction wct, @NonNull Activity primaryActivity, @NonNull Intent secondaryIntent, @NonNull SplitPairRule rule) { final Rect parentBounds = getParentContainerBounds(primaryActivity); + final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair( + primaryActivity, secondaryIntent); final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule, - isLtr(primaryActivity, rule)); + primaryActivity, minDimensionsPair); final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct, primaryActivity, primaryRectBounds, null); @@ -113,7 +128,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { final TaskFragmentContainer secondaryContainer = mController.newContainer( secondaryIntent, primaryActivity, taskId); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, - rule, isLtr(primaryActivity, rule)); + rule, primaryActivity, minDimensionsPair); final int windowingMode = mController.getTaskContainer(taskId) .getWindowingModeForSplitTaskFragment(secondaryRectBounds); createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(), @@ -121,7 +136,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { windowingMode); // Set adjacent to each other so that the containers below will be invisible. - setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); + setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule, + minDimensionsPair); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); @@ -144,13 +160,15 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect parentBounds = getParentContainerBounds(primaryActivity); + final Pair<Size, Size> minDimensionsPair = getActivitiesMinDimensionsPair(primaryActivity, + secondaryActivity); final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule, - isLtr(primaryActivity, rule)); + primaryActivity, minDimensionsPair); final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct, primaryActivity, primaryRectBounds, null); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule, - isLtr(primaryActivity, rule)); + primaryActivity, minDimensionsPair); final TaskFragmentContainer curSecondaryContainer = mController.getContainerWithActivity( secondaryActivity); TaskFragmentContainer containerToAvoid = primaryContainer; @@ -162,7 +180,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { secondaryActivity, secondaryRectBounds, containerToAvoid); // Set adjacent to each other so that the containers below will be invisible. - setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); + setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule, + minDimensionsPair); mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule); @@ -211,10 +230,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { void startActivityToSide(@NonNull Activity launchingActivity, @NonNull Intent activityIntent, @Nullable Bundle activityOptions, @NonNull SplitRule rule, boolean isPlaceholder) { final Rect parentBounds = getParentContainerBounds(launchingActivity); + final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair( + launchingActivity, activityIntent); final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule, - isLtr(launchingActivity, rule)); + launchingActivity, minDimensionsPair); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule, - isLtr(launchingActivity, rule)); + launchingActivity, minDimensionsPair); TaskFragmentContainer primaryContainer = mController.getContainerWithActivity( launchingActivity); @@ -258,11 +279,11 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { if (activity == null) { return; } - final boolean isLtr = isLtr(activity, rule); + final Pair<Size, Size> minDimensionsPair = splitContainer.getMinDimensionsPair(); final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule, - isLtr); + activity, minDimensionsPair); final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule, - isLtr); + activity, minDimensionsPair); final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer(); // Whether the placeholder is becoming side-by-side with the primary from fullscreen. final boolean isPlaceholderBecomingSplit = splitContainer.isPlaceholderContainer() @@ -273,7 +294,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // are created again. resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds); resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds); - setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule); + setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule, + minDimensionsPair); if (isPlaceholderBecomingSplit) { // When placeholder is shown in split, we should keep the focus on the primary. wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken()); @@ -287,11 +309,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct, @NonNull TaskFragmentContainer primaryContainer, - @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule) { + @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, + @NonNull Pair<Size, Size> minDimensionsPair) { final Rect parentBounds = getParentContainerBounds(primaryContainer); // Clear adjacent TaskFragments if the container is shown in fullscreen, or the // secondaryContainer could not be finished. - if (!shouldShowSideBySide(parentBounds, splitRule)) { + if (!shouldShowSideBySide(parentBounds, splitRule, minDimensionsPair)) { setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(), null /* secondary */, null /* splitRule */); } else { @@ -373,41 +396,132 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { super.updateWindowingMode(wct, fragmentToken, windowingMode); } - boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) { + static boolean shouldShowSideBySide(@NonNull Rect parentBounds, @NonNull SplitRule rule) { + return shouldShowSideBySide(parentBounds, rule, null /* minimumDimensionPair */); + } + + static boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) { final Rect parentBounds = getParentContainerBounds(splitContainer.getPrimaryContainer()); - return shouldShowSideBySide(parentBounds, splitContainer.getSplitRule()); + + return shouldShowSideBySide(parentBounds, splitContainer.getSplitRule(), + splitContainer.getMinDimensionsPair()); } - boolean shouldShowSideBySide(@Nullable Rect parentBounds, @NonNull SplitRule rule) { + static boolean shouldShowSideBySide(@NonNull Rect parentBounds, @NonNull SplitRule rule, + @Nullable Pair<Size, Size> minDimensionsPair) { // TODO(b/190433398): Supply correct insets. final WindowMetrics parentMetrics = new WindowMetrics(parentBounds, new WindowInsets(new Rect())); - return rule.checkParentMetrics(parentMetrics); + // Don't show side by side if bounds is not qualified. + if (!rule.checkParentMetrics(parentMetrics)) { + return false; + } + final float splitRatio = rule.getSplitRatio(); + // We only care the size of the bounds regardless of its position. + final Rect primaryBounds = getPrimaryBounds(parentBounds, splitRatio, true /* isLtr */); + final Rect secondaryBounds = getSecondaryBounds(parentBounds, splitRatio, true /* isLtr */); + + if (minDimensionsPair == null) { + return true; + } + return !boundsSmallerThanMinDimensions(primaryBounds, minDimensionsPair.first) + && !boundsSmallerThanMinDimensions(secondaryBounds, minDimensionsPair.second); } @NonNull - private Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds, - @NonNull SplitRule rule, boolean isLtr) { - if (!shouldShowSideBySide(parentBounds, rule)) { - return new Rect(); + static Pair<Size, Size> getActivitiesMinDimensionsPair(Activity primaryActivity, + Activity secondaryActivity) { + return new Pair<>(getMinDimensions(primaryActivity), getMinDimensions(secondaryActivity)); + } + + @NonNull + static Pair<Size, Size> getActivityIntentMinDimensionsPair(Activity primaryActivity, + Intent secondaryIntent) { + return new Pair<>(getMinDimensions(primaryActivity), getMinDimensions(secondaryIntent)); + } + + @Nullable + static Size getMinDimensions(@Nullable Activity activity) { + if (activity == null) { + return null; + } + final ActivityInfo.WindowLayout windowLayout = activity.getActivityInfo().windowLayout; + if (windowLayout == null) { + return null; } + return new Size(windowLayout.minWidth, windowLayout.minHeight); + } + // TODO(b/232871351): find a light-weight approach for this check. + @Nullable + static Size getMinDimensions(@Nullable Intent intent) { + if (intent == null) { + return null; + } + final PackageManager packageManager = ActivityThread.currentActivityThread() + .getApplication().getPackageManager(); + final ResolveInfo resolveInfo = packageManager.resolveActivity(intent, + PackageManager.ResolveInfoFlags.of(MATCH_ALL)); + if (resolveInfo == null) { + return null; + } + final ActivityInfo activityInfo = resolveInfo.activityInfo; + if (activityInfo == null) { + return null; + } + final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout; + if (windowLayout == null) { + return null; + } + return new Size(windowLayout.minWidth, windowLayout.minHeight); + } + + static boolean boundsSmallerThanMinDimensions(@NonNull Rect bounds, + @Nullable Size minDimensions) { + if (minDimensions == null) { + return false; + } + return bounds.width() < minDimensions.getWidth() + || bounds.height() < minDimensions.getHeight(); + } + + @VisibleForTesting + @NonNull + static Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds, + @NonNull SplitRule rule, @NonNull Activity primaryActivity, + @Nullable Pair<Size, Size> minDimensionsPair) { + if (!shouldShowSideBySide(parentBounds, rule, minDimensionsPair)) { + return new Rect(); + } + final boolean isLtr = isLtr(primaryActivity, rule); final float splitRatio = rule.getSplitRatio(); - final float rtlSplitRatio = 1 - splitRatio; + switch (position) { case POSITION_START: - return isLtr ? getLeftContainerBounds(parentBounds, splitRatio) - : getRightContainerBounds(parentBounds, rtlSplitRatio); + return getPrimaryBounds(parentBounds, splitRatio, isLtr); case POSITION_END: - return isLtr ? getRightContainerBounds(parentBounds, splitRatio) - : getLeftContainerBounds(parentBounds, rtlSplitRatio); + return getSecondaryBounds(parentBounds, splitRatio, isLtr); case POSITION_FILL: - return parentBounds; + default: + return new Rect(); } - return parentBounds; } - private Rect getLeftContainerBounds(@NonNull Rect parentBounds, float splitRatio) { + @NonNull + private static Rect getPrimaryBounds(@NonNull Rect parentBounds, float splitRatio, + boolean isLtr) { + return isLtr ? getLeftContainerBounds(parentBounds, splitRatio) + : getRightContainerBounds(parentBounds, 1 - splitRatio); + } + + @NonNull + private static Rect getSecondaryBounds(@NonNull Rect parentBounds, float splitRatio, + boolean isLtr) { + return isLtr ? getRightContainerBounds(parentBounds, splitRatio) + : getLeftContainerBounds(parentBounds, 1 - splitRatio); + } + + private static Rect getLeftContainerBounds(@NonNull Rect parentBounds, float splitRatio) { return new Rect( parentBounds.left, parentBounds.top, @@ -415,7 +529,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { parentBounds.bottom); } - private Rect getRightContainerBounds(@NonNull Rect parentBounds, float splitRatio) { + private static Rect getRightContainerBounds(@NonNull Rect parentBounds, float splitRatio) { return new Rect( (int) (parentBounds.left + parentBounds.width() * splitRatio), parentBounds.top, @@ -427,7 +541,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { * Checks if a split with the provided rule should be displays in left-to-right layout * direction, either always or with the current configuration. */ - private boolean isLtr(@NonNull Context context, @NonNull SplitRule rule) { + private static boolean isLtr(@NonNull Context context, @NonNull SplitRule rule) { switch (rule.getLayoutDirection()) { case LayoutDirection.LOCALE: return context.getResources().getConfiguration().getLayoutDirection() @@ -441,7 +555,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { } @NonNull - Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) { + static Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) { return container.getTaskContainer().getTaskBounds(); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 624cde50ff72..abf32a26efa2 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -26,6 +26,7 @@ import android.content.Intent; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; +import android.util.Size; import android.window.TaskFragmentInfo; import android.window.WindowContainerTransaction; @@ -414,6 +415,11 @@ class TaskFragmentContainer { } } + @NonNull + Rect getLastRequestedBounds() { + return mLastRequestedBounds; + } + /** * Checks if last requested windowing mode is equal to the provided value. */ @@ -439,6 +445,31 @@ class TaskFragmentContainer { return mTaskContainer; } + @Nullable + Size getMinDimensions() { + if (mInfo == null) { + return null; + } + int maxMinWidth = mInfo.getMinimumWidth(); + int maxMinHeight = mInfo.getMinimumHeight(); + for (Activity activity : mPendingAppearedActivities) { + final Size minDimensions = SplitPresenter.getMinDimensions(activity); + if (minDimensions == null) { + continue; + } + maxMinWidth = Math.max(maxMinWidth, minDimensions.getWidth()); + maxMinHeight = Math.max(maxMinHeight, minDimensions.getHeight()); + } + if (mPendingAppearedIntent != null) { + final Size minDimensions = SplitPresenter.getMinDimensions(mPendingAppearedIntent); + if (minDimensions != null) { + maxMinWidth = Math.max(maxMinWidth, minDimensions.getWidth()); + maxMinHeight = Math.max(maxMinHeight, minDimensions.getHeight()); + } + } + return new Size(maxMinWidth, maxMinHeight); + } + @Override public String toString() { return toString(true /* includeContainersToFinishOnExit */); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index a6f638822d10..cfb32050e32f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -43,7 +43,6 @@ import androidx.window.util.DataProducer; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.Set; import java.util.function.Consumer; @@ -63,7 +62,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer; - public WindowLayoutComponentImpl(Context context) { + public WindowLayoutComponentImpl(@NonNull Context context) { ((Application) context.getApplicationContext()) .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); RawFoldingFeatureProducer foldingFeatureProducer = new RawFoldingFeatureProducer(context); @@ -80,8 +79,12 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { */ public void addWindowLayoutInfoListener(@NonNull Activity activity, @NonNull Consumer<WindowLayoutInfo> consumer) { + mFoldingFeatureProducer.getData((features) -> { + // Get the WindowLayoutInfo from the activity and pass the value to the layoutConsumer. + WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(activity, features); + consumer.accept(newWindowLayout); + }); mWindowLayoutChangeListeners.put(activity, consumer); - onDisplayFeaturesChanged(); } /** @@ -89,18 +92,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { * * @param consumer no longer interested in receiving updates to {@link WindowLayoutInfo} */ - public void removeWindowLayoutInfoListener( - @NonNull Consumer<WindowLayoutInfo> consumer) { + public void removeWindowLayoutInfoListener(@NonNull Consumer<WindowLayoutInfo> consumer) { mWindowLayoutChangeListeners.values().remove(consumer); - onDisplayFeaturesChanged(); - } - - void updateWindowLayout(@NonNull Activity activity, - @NonNull WindowLayoutInfo newLayout) { - Consumer<WindowLayoutInfo> consumer = mWindowLayoutChangeListeners.get(activity); - if (consumer != null) { - consumer.accept(newLayout); - } } @NonNull @@ -108,7 +101,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { return mWindowLayoutChangeListeners.keySet(); } - @NonNull private boolean isListeningForLayoutChanges(IBinder token) { for (Activity activity: getActivitiesListeningForLayoutChanges()) { if (token.equals(activity.getWindow().getAttributes().token)) { @@ -125,12 +117,12 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { /** * A convenience method to translate from the common feature state to the extensions feature * state. More specifically, translates from {@link CommonFoldingFeature.State} to - * {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED}. If it is not + * {@link FoldingFeature#STATE_FLAT} or {@link FoldingFeature#STATE_HALF_OPENED}. If it is not * possible to translate, then we will return a {@code null} value. * * @param state if it matches a value in {@link CommonFoldingFeature.State}, {@code null} - * otherwise. @return a {@link FoldingFeature.STATE_FLAT} or - * {@link FoldingFeature.STATE_HALF_OPENED} if the given state matches a value in + * otherwise. @return a {@link FoldingFeature#STATE_FLAT} or + * {@link FoldingFeature#STATE_HALF_OPENED} if the given state matches a value in * {@link CommonFoldingFeature.State} and {@code null} otherwise. */ @Nullable @@ -144,17 +136,24 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } } - private void onDisplayFeaturesChanged() { + private void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) { for (Activity activity : getActivitiesListeningForLayoutChanges()) { - WindowLayoutInfo newLayout = getWindowLayoutInfo(activity); - updateWindowLayout(activity, newLayout); + // Get the WindowLayoutInfo from the activity and pass the value to the layoutConsumer. + Consumer<WindowLayoutInfo> layoutConsumer = mWindowLayoutChangeListeners.get(activity); + WindowLayoutInfo newWindowLayout = getWindowLayoutInfo(activity, storedFeatures); + layoutConsumer.accept(newWindowLayout); } } - @NonNull - private WindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) { - List<DisplayFeature> displayFeatures = getDisplayFeatures(activity); - return new WindowLayoutInfo(displayFeatures); + /** + * Translates the {@link DisplayFeature} into a {@link WindowLayoutInfo} when a + * valid state is found. + * @param activity a proxy for the {@link android.view.Window} that contains the + */ + private WindowLayoutInfo getWindowLayoutInfo( + @NonNull Activity activity, List<CommonFoldingFeature> storedFeatures) { + List<DisplayFeature> displayFeatureList = getDisplayFeatures(activity, storedFeatures); + return new WindowLayoutInfo(displayFeatureList); } /** @@ -172,26 +171,21 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { * * @param activity a proxy for the {@link android.view.Window} that contains the * {@link DisplayFeature}. - * @return a {@link List} of valid {@link DisplayFeature} that * are within the {@link android.view.Window} of the {@link Activity} */ - private List<DisplayFeature> getDisplayFeatures(@NonNull Activity activity) { + private List<DisplayFeature> getDisplayFeatures( + @NonNull Activity activity, List<CommonFoldingFeature> storedFeatures) { List<DisplayFeature> features = new ArrayList<>(); int displayId = activity.getDisplay().getDisplayId(); if (displayId != DEFAULT_DISPLAY) { Log.w(TAG, "This sample doesn't support display features on secondary displays"); return features; - } - - if (activity.isInMultiWindowMode()) { + } else if (activity.isInMultiWindowMode()) { // It is recommended not to report any display features in multi-window mode, since it // won't be possible to synchronize the display feature positions with window movement. return features; - } - - Optional<List<CommonFoldingFeature>> storedFeatures = mFoldingFeatureProducer.getData(); - if (storedFeatures.isPresent()) { - for (CommonFoldingFeature baseFeature : storedFeatures.get()) { + } else { + for (CommonFoldingFeature baseFeature : storedFeatures) { Integer state = convertToExtensionState(baseFeature.getState()); if (state == null) { continue; @@ -205,8 +199,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { features.add(new FoldingFeature(featureRect, baseFeature.getType(), state)); } } + return features; } - return features; } /** @@ -233,7 +227,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private void onDisplayFeaturesChangedIfListening(Activity activity) { IBinder token = activity.getWindow().getAttributes().token; if (token == null || isListeningForLayoutChanges(token)) { - onDisplayFeaturesChanged(); + mFoldingFeatureProducer.getData( + WindowLayoutComponentImpl.this::onDisplayFeaturesChanged); } } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java index 970f0a2af632..5bfb0ebdcaa8 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java @@ -28,41 +28,42 @@ import android.content.Context; import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; -import android.util.Log; import androidx.annotation.NonNull; import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.EmptyLifecycleCallbacksAdapter; import androidx.window.common.RawFoldingFeatureProducer; -import androidx.window.util.DataProducer; +import androidx.window.util.BaseDataProducer; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Optional; /** * Reference implementation of androidx.window.sidecar OEM interface for use with * WindowManager Jetpack. */ class SampleSidecarImpl extends StubSidecar { - private static final String TAG = "SampleSidecar"; - - private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer; - + private List<CommonFoldingFeature> mStoredFeatures = new ArrayList<>(); SampleSidecarImpl(Context context) { ((Application) context.getApplicationContext()) .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); - DataProducer<String> settingsFeatureProducer = new RawFoldingFeatureProducer(context); - mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context, - settingsFeatureProducer); + BaseDataProducer<String> settingsFeatureProducer = new RawFoldingFeatureProducer(context); + BaseDataProducer<List<CommonFoldingFeature>> foldingFeatureProducer = + new DeviceStateManagerFoldingFeatureProducer(context, + settingsFeatureProducer); - mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); + foldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); } - private void onDisplayFeaturesChanged() { + private void setStoredFeatures(List<CommonFoldingFeature> storedFeatures) { + mStoredFeatures = storedFeatures; + } + + private void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) { + setStoredFeatures(storedFeatures); updateDeviceState(getDeviceState()); for (IBinder windowToken : getWindowsListeningForLayoutChanges()) { SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken); @@ -79,16 +80,16 @@ class SampleSidecarImpl extends StubSidecar { } private int deviceStateFromFeature() { - List<CommonFoldingFeature> storedFeatures = mFoldingFeatureProducer.getData() - .orElse(Collections.emptyList()); - for (int i = 0; i < storedFeatures.size(); i++) { - CommonFoldingFeature feature = storedFeatures.get(i); + for (int i = 0; i < mStoredFeatures.size(); i++) { + CommonFoldingFeature feature = mStoredFeatures.get(i); final int state = feature.getState(); switch (state) { case CommonFoldingFeature.COMMON_STATE_FLAT: return SidecarDeviceState.POSTURE_OPENED; case CommonFoldingFeature.COMMON_STATE_HALF_OPENED: return SidecarDeviceState.POSTURE_HALF_OPENED; + case CommonFoldingFeature.COMMON_STATE_UNKNOWN: + return SidecarDeviceState.POSTURE_UNKNOWN; } } return SidecarDeviceState.POSTURE_UNKNOWN; @@ -109,7 +110,6 @@ class SampleSidecarImpl extends StubSidecar { private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) { int displayId = activity.getDisplay().getDisplayId(); if (displayId != DEFAULT_DISPLAY) { - Log.w(TAG, "This sample doesn't support display features on secondary displays"); return Collections.emptyList(); } @@ -119,18 +119,15 @@ class SampleSidecarImpl extends StubSidecar { return Collections.emptyList(); } - Optional<List<CommonFoldingFeature>> storedFeatures = mFoldingFeatureProducer.getData(); List<SidecarDisplayFeature> features = new ArrayList<>(); - if (storedFeatures.isPresent()) { - for (CommonFoldingFeature baseFeature : storedFeatures.get()) { - SidecarDisplayFeature feature = new SidecarDisplayFeature(); - Rect featureRect = baseFeature.getRect(); - rotateRectToDisplayRotation(displayId, featureRect); - transformToWindowSpaceRect(activity, featureRect); - feature.setRect(featureRect); - feature.setType(baseFeature.getType()); - features.add(feature); - } + for (CommonFoldingFeature baseFeature : mStoredFeatures) { + SidecarDisplayFeature feature = new SidecarDisplayFeature(); + Rect featureRect = baseFeature.getRect(); + rotateRectToDisplayRotation(displayId, featureRect); + transformToWindowSpaceRect(activity, featureRect); + feature.setRect(featureRect); + feature.setType(baseFeature.getType()); + features.add(feature); } return Collections.unmodifiableList(features); } @@ -138,7 +135,7 @@ class SampleSidecarImpl extends StubSidecar { @Override protected void onListenersChanged() { if (hasListeners()) { - onDisplayFeaturesChanged(); + onDisplayFeaturesChanged(mStoredFeatures); } } @@ -158,7 +155,7 @@ class SampleSidecarImpl extends StubSidecar { private void onDisplayFeaturesChangedForActivity(@NonNull Activity activity) { IBinder token = activity.getWindow().getAttributes().token; if (token == null || mWindowLayoutChangeListenerTokens.contains(token)) { - onDisplayFeaturesChanged(); + onDisplayFeaturesChanged(mStoredFeatures); } } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java new file mode 100644 index 000000000000..7624b693ac43 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/util/AcceptOnceConsumer.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 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 androidx.window.util; + +import android.annotation.NonNull; + +import java.util.function.Consumer; + +/** + * A base class that works with {@link BaseDataProducer} to add/remove a consumer that should + * only be used once when {@link BaseDataProducer#notifyDataChanged} is called. + * @param <T> The type of data this producer returns through {@link DataProducer#getData}. + */ +public class AcceptOnceConsumer<T> implements Consumer<T> { + private final Consumer<T> mCallback; + private final DataProducer<T> mProducer; + + public AcceptOnceConsumer(@NonNull DataProducer<T> producer, @NonNull Consumer<T> callback) { + mProducer = producer; + mCallback = callback; + } + + @Override + public void accept(@NonNull T t) { + mCallback.accept(t); + mProducer.removeDataChangedCallback(this); + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java index 930db3b701b7..0da44ac36a6e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java @@ -19,38 +19,48 @@ package androidx.window.util; import androidx.annotation.NonNull; import java.util.LinkedHashSet; +import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; /** * Base class that provides the implementation for the callback mechanism of the * {@link DataProducer} API. * - * @param <T> The type of data this producer returns through {@link #getData()}. + * @param <T> The type of data this producer returns through {@link DataProducer#getData}. */ public abstract class BaseDataProducer<T> implements DataProducer<T> { - private final Set<Runnable> mCallbacks = new LinkedHashSet<>(); + private final Set<Consumer<T>> mCallbacks = new LinkedHashSet<>(); @Override - public final void addDataChangedCallback(@NonNull Runnable callback) { + public final void addDataChangedCallback(@NonNull Consumer<T> callback) { mCallbacks.add(callback); + Optional<T> currentData = getCurrentData(); + currentData.ifPresent(callback); onListenersChanged(mCallbacks); } @Override - public final void removeDataChangedCallback(@NonNull Runnable callback) { + public final void removeDataChangedCallback(@NonNull Consumer<T> callback) { mCallbacks.remove(callback); onListenersChanged(mCallbacks); } - protected void onListenersChanged(Set<Runnable> callbacks) {} + protected void onListenersChanged(Set<Consumer<T>> callbacks) {} /** - * Called to notify all registered callbacks that the data provided by {@link #getData()} has - * changed. + * @return the current data if available and {@code Optional.empty()} otherwise. */ - protected void notifyDataChanged() { - for (Runnable callback : mCallbacks) { - callback.run(); + @NonNull + public abstract Optional<T> getCurrentData(); + + /** + * Called to notify all registered consumers that the data provided + * by {@link DataProducer#getData} has changed. + */ + protected void notifyDataChanged(T value) { + for (Consumer<T> callback : mCallbacks) { + callback.accept(value); } } -} +}
\ No newline at end of file diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java index d4d1a23b756b..ec301dc34aaa 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java @@ -18,26 +18,27 @@ package androidx.window.util; import android.annotation.NonNull; -import java.util.Optional; +import java.util.function.Consumer; /** - * Produces data through {@link #getData()} and provides a mechanism for receiving a callback when - * the data managed by the produces has changed. + * Produces data through {@link DataProducer#getData} and provides a mechanism for receiving + * a callback when the data managed by the produces has changed. * - * @param <T> The type of data this producer returns through {@link #getData()}. + * @param <T> The type of data this producer returns through {@link DataProducer#getData}. */ public interface DataProducer<T> { /** - * Returns the data currently stored in the provider, or {@link Optional#empty()} if the - * provider has no data. + * Emits the first available data at that point in time. + * @param dataConsumer a {@link Consumer} that will receive one value. */ - Optional<T> getData(); + void getData(@NonNull Consumer<T> dataConsumer); /** - * Adds a callback to be notified when the data returned from {@link #getData()} has changed. + * Adds a callback to be notified when the data returned + * from {@link DataProducer#getData} has changed. */ - void addDataChangedCallback(@NonNull Runnable callback); + void addDataChangedCallback(@NonNull Consumer<T> callback); - /** Removes a callback previously added with {@link #addDataChangedCallback(Runnable)}. */ - void removeDataChangedCallback(@NonNull Runnable callback); + /** Removes a callback previously added with {@link #addDataChangedCallback(Consumer)}. */ + void removeDataChangedCallback(@NonNull Consumer<T> callback); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/AndroidManifest.xml b/libs/WindowManager/Jetpack/tests/unittest/AndroidManifest.xml index b12b6f6f0ef1..c736e9ed971e 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/AndroidManifest.xml +++ b/libs/WindowManager/Jetpack/tests/unittest/AndroidManifest.xml @@ -22,6 +22,11 @@ <application android:debuggable="true" android:largeHeap="true"> <uses-library android:name="android.test.mock" /> <uses-library android:name="android.test.runner" /> + + <activity android:name="androidx.window.extensions.embedding.MinimumDimensionActivity"> + <layout android:minWidth="600px" + android:minHeight="1200px"/> + </activity> </application> <instrumentation diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java new file mode 100644 index 000000000000..835c40365cda --- /dev/null +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2022 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 androidx.window.extensions.embedding; + +import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS; +import static androidx.window.extensions.embedding.SplitRule.FINISH_NEVER; + +import static org.mockito.Mockito.mock; + +import android.annotation.NonNull; +import android.app.Activity; +import android.content.Intent; +import android.content.res.Configuration; +import android.graphics.Point; +import android.graphics.Rect; +import android.util.Pair; +import android.window.TaskFragmentInfo; +import android.window.WindowContainerToken; + +import java.util.Collections; + +public class EmbeddingTestUtils { + static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200); + static final int TASK_ID = 10; + static final float SPLIT_RATIO = 0.5f; + /** Default finish behavior in Jetpack. */ + static final int DEFAULT_FINISH_PRIMARY_WITH_SECONDARY = FINISH_NEVER; + static final int DEFAULT_FINISH_SECONDARY_WITH_PRIMARY = FINISH_ALWAYS; + + private EmbeddingTestUtils() {} + + /** Gets the bounds of a TaskFragment that is in split. */ + static Rect getSplitBounds(boolean isPrimary) { + final int width = (int) (TASK_BOUNDS.width() * SPLIT_RATIO); + return isPrimary + ? new Rect(TASK_BOUNDS.left, TASK_BOUNDS.top, TASK_BOUNDS.left + width, + TASK_BOUNDS.bottom) + : new Rect( + TASK_BOUNDS.left + width, TASK_BOUNDS.top, TASK_BOUNDS.right, + TASK_BOUNDS.bottom); + } + + /** Creates a rule to always split the given activity and the given intent. */ + static SplitRule createSplitRule(@NonNull Activity primaryActivity, + @NonNull Intent secondaryIntent) { + final Pair<Activity, Intent> targetPair = new Pair<>(primaryActivity, secondaryIntent); + return new SplitPairRule.Builder( + activityPair -> false, + targetPair::equals, + w -> true) + .setSplitRatio(SPLIT_RATIO) + .setShouldClearTop(true) + .build(); + } + + /** Creates a rule to always split the given activities. */ + static SplitRule createSplitRule(@NonNull Activity primaryActivity, + @NonNull Activity secondaryActivity) { + return createSplitRule(primaryActivity, secondaryActivity, + DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY, + true /* clearTop */); + } + + /** Creates a rule to always split the given activities with the given finish behaviors. */ + static SplitRule createSplitRule(@NonNull Activity primaryActivity, + @NonNull Activity secondaryActivity, int finishPrimaryWithSecondary, + int finishSecondaryWithPrimary, boolean clearTop) { + final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity); + return new SplitPairRule.Builder( + targetPair::equals, + activityIntentPair -> false, + w -> true) + .setSplitRatio(SPLIT_RATIO) + .setFinishPrimaryWithSecondary(finishPrimaryWithSecondary) + .setFinishSecondaryWithPrimary(finishSecondaryWithPrimary) + .setShouldClearTop(clearTop) + .build(); + } + + /** Creates a mock TaskFragmentInfo for the given TaskFragment. */ + static TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container, + @NonNull Activity activity) { + return new TaskFragmentInfo(container.getTaskFragmentToken(), + mock(WindowContainerToken.class), + new Configuration(), + 1, + true /* isVisible */, + Collections.singletonList(activity.getActivityToken()), + new Point(), + false /* isTaskClearedForReuse */, + false /* isTaskFragmentClearedForPip */, + new Point()); + } +} diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java index a191e685f651..4d2595275f20 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java @@ -18,6 +18,8 @@ package androidx.window.extensions.embedding; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -58,8 +60,6 @@ import java.util.ArrayList; @SmallTest @RunWith(AndroidJUnit4.class) public class JetpackTaskFragmentOrganizerTest { - private static final int TASK_ID = 10; - @Mock private WindowContainerTransaction mTransaction; @Mock @@ -131,6 +131,7 @@ public class JetpackTaskFragmentOrganizerTest { return new TaskFragmentInfo(container.getTaskFragmentToken(), mock(WindowContainerToken.class), new Configuration(), 0 /* runningActivityCount */, false /* isVisible */, new ArrayList<>(), new Point(), - false /* isTaskClearedForReuse */, false /* isTaskFragmentClearedForPip */); + false /* isTaskClearedForReuse */, false /* isTaskFragmentClearedForPip */, + new Point()); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerState.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/MinimumDimensionActivity.java index af2ab158ab46..ffcaf3e6f546 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerState.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/MinimumDimensionActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2022 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. @@ -14,12 +14,12 @@ * limitations under the License. */ -package com.android.wm.shell.legacysplitscreen; +package androidx.window.extensions.embedding; + +import android.app.Activity; /** - * Class to hold state of divider that needs to persist across configuration changes. + * Activity that declares minWidth and minHeight in + * {@link android.content.pm.ActivityInfo.WindowLayout} */ -final class DividerState { - public boolean animateAfterRecentsDrawn; - public float mRatioPositionBeforeMinimized; -} +public class MinimumDimensionActivity extends Activity {} diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 60390eb2b3d2..ef7728cec387 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -19,8 +19,13 @@ package androidx.window.extensions.embedding; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.SPLIT_RATIO; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds; import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS; -import static androidx.window.extensions.embedding.SplitRule.FINISH_NEVER; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -49,20 +54,19 @@ import android.app.Activity; import android.app.ActivityOptions; import android.content.ComponentName; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.platform.test.annotations.Presubmit; -import android.util.Pair; import android.window.TaskFragmentInfo; -import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -86,16 +90,9 @@ import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class SplitControllerTest { - private static final int TASK_ID = 10; - private static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200); - private static final float SPLIT_RATIO = 0.5f; private static final Intent PLACEHOLDER_INTENT = new Intent().setComponent( new ComponentName("test", "placeholder")); - /** Default finish behavior in Jetpack. */ - private static final int DEFAULT_FINISH_PRIMARY_WITH_SECONDARY = FINISH_NEVER; - private static final int DEFAULT_FINISH_SECONDARY_WITH_PRIMARY = FINISH_ALWAYS; - private Activity mActivity; @Mock private Resources mActivityResources; @@ -420,6 +417,25 @@ public class SplitControllerTest { } @Test + public void testResolveStartActivityIntent_shouldLaunchInFullscreen() { + final Intent intent = new Intent().setComponent( + new ComponentName(ApplicationProvider.getApplicationContext(), + MinimumDimensionActivity.class)); + setupSplitRule(mActivity, intent); + final Activity primaryActivity = createMockActivity(); + addSplitTaskFragments(primaryActivity, mActivity); + + final TaskFragmentContainer container = mSplitController.resolveStartActivityIntent( + mTransaction, TASK_ID, intent, null /* launchingActivity */); + final TaskFragmentContainer primaryContainer = mSplitController.getContainerWithActivity( + mActivity); + + assertNotNull(mSplitController.getActiveSplitForContainers(primaryContainer, container)); + assertTrue(primaryContainer.areLastRequestedBoundsEqual(null)); + assertTrue(container.areLastRequestedBoundsEqual(null)); + } + + @Test public void testPlaceActivityInTopContainer() { mSplitController.placeActivityInTopContainer(mActivity); @@ -767,6 +783,52 @@ public class SplitControllerTest { } @Test + public void testResolveActivityToContainer_primaryActivityMinDimensionsNotSatisfied() { + final Activity activityBelow = createMockActivity(); + setupSplitRule(mActivity, activityBelow); + + ActivityInfo aInfo = new ActivityInfo(); + final Rect primaryBounds = getSplitBounds(true /* isPrimary */); + aInfo.windowLayout = new ActivityInfo.WindowLayout(0, 0, 0, 0, 0, + primaryBounds.width() + 1, primaryBounds.height() + 1); + doReturn(aInfo).when(mActivity).getActivityInfo(); + + final TaskFragmentContainer container = mSplitController.newContainer(activityBelow, + TASK_ID); + container.addPendingAppearedActivity(mActivity); + + // Allow to split as primary. + boolean result = mSplitController.resolveActivityToContainer(mActivity, + true /* isOnReparent */); + + assertTrue(result); + assertSplitPair(mActivity, activityBelow, true /* matchParentBounds */); + } + + @Test + public void testResolveActivityToContainer_secondaryActivityMinDimensionsNotSatisfied() { + final Activity activityBelow = createMockActivity(); + setupSplitRule(activityBelow, mActivity); + + ActivityInfo aInfo = new ActivityInfo(); + final Rect secondaryBounds = getSplitBounds(false /* isPrimary */); + aInfo.windowLayout = new ActivityInfo.WindowLayout(0, 0, 0, 0, 0, + secondaryBounds.width() + 1, secondaryBounds.height() + 1); + doReturn(aInfo).when(mActivity).getActivityInfo(); + + final TaskFragmentContainer container = mSplitController.newContainer(activityBelow, + TASK_ID); + container.addPendingAppearedActivity(mActivity); + + // Allow to split as primary. + boolean result = mSplitController.resolveActivityToContainer(mActivity, + false /* isOnReparent */); + + assertTrue(result); + assertSplitPair(activityBelow, mActivity, true /* matchParentBounds */); + } + + @Test public void testResolveActivityToContainer_inUnknownTaskFragment() { doReturn(new Binder()).when(mSplitController).getInitialTaskFragmentToken(mActivity); @@ -835,23 +897,10 @@ public class SplitControllerTest { doReturn(activityToken).when(activity).getActivityToken(); doReturn(activity).when(mSplitController).getActivity(activityToken); doReturn(TASK_ID).when(activity).getTaskId(); + doReturn(new ActivityInfo()).when(activity).getActivityInfo(); return activity; } - /** Creates a mock TaskFragmentInfo for the given TaskFragment. */ - private TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container, - @NonNull Activity activity) { - return new TaskFragmentInfo(container.getTaskFragmentToken(), - mock(WindowContainerToken.class), - new Configuration(), - 1, - true /* isVisible */, - Collections.singletonList(activity.getActivityToken()), - new Point(), - false /* isTaskClearedForReuse */, - false /* isTaskFragmentClearedForPip */); - } - /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */ private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) { final TaskFragmentContainer container = mSplitController.newContainer(activity, TASK_ID); @@ -902,49 +951,10 @@ public class SplitControllerTest { /** Setups a rule to always split the given activities. */ private void setupSplitRule(@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) { - final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity, - DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY, - true /* clearTop */); + final SplitRule splitRule = createSplitRule(primaryActivity, secondaryActivity); mSplitController.setEmbeddingRules(Collections.singleton(splitRule)); } - /** Creates a rule to always split the given activity and the given intent. */ - private SplitRule createSplitRule(@NonNull Activity primaryActivity, - @NonNull Intent secondaryIntent) { - final Pair<Activity, Intent> targetPair = new Pair<>(primaryActivity, secondaryIntent); - return new SplitPairRule.Builder( - activityPair -> false, - targetPair::equals, - w -> true) - .setSplitRatio(SPLIT_RATIO) - .setShouldClearTop(true) - .build(); - } - - /** Creates a rule to always split the given activities. */ - private SplitRule createSplitRule(@NonNull Activity primaryActivity, - @NonNull Activity secondaryActivity) { - return createSplitRule(primaryActivity, secondaryActivity, - DEFAULT_FINISH_PRIMARY_WITH_SECONDARY, DEFAULT_FINISH_SECONDARY_WITH_PRIMARY, - true /* clearTop */); - } - - /** Creates a rule to always split the given activities with the given finish behaviors. */ - private SplitRule createSplitRule(@NonNull Activity primaryActivity, - @NonNull Activity secondaryActivity, int finishPrimaryWithSecondary, - int finishSecondaryWithPrimary, boolean clearTop) { - final Pair<Activity, Activity> targetPair = new Pair<>(primaryActivity, secondaryActivity); - return new SplitPairRule.Builder( - targetPair::equals, - activityIntentPair -> false, - w -> true) - .setSplitRatio(SPLIT_RATIO) - .setFinishPrimaryWithSecondary(finishPrimaryWithSecondary) - .setFinishSecondaryWithPrimary(finishSecondaryWithPrimary) - .setShouldClearTop(clearTop) - .build(); - } - /** Adds a pair of TaskFragments as split for the given activities. */ private void addSplitTaskFragments(@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) { @@ -973,39 +983,42 @@ public class SplitControllerTest { secondaryContainer.setLastRequestedBounds(getSplitBounds(false /* isPrimary */)); } - /** Gets the bounds of a TaskFragment that is in split. */ - private Rect getSplitBounds(boolean isPrimary) { - final int width = (int) (TASK_BOUNDS.width() * SPLIT_RATIO); - return isPrimary - ? new Rect(TASK_BOUNDS.left, TASK_BOUNDS.top, TASK_BOUNDS.left + width, - TASK_BOUNDS.bottom) - : new Rect(TASK_BOUNDS.left + width, TASK_BOUNDS.top, TASK_BOUNDS.right, - TASK_BOUNDS.bottom); + /** Asserts that the two given activities are in split. */ + private void assertSplitPair(@NonNull Activity primaryActivity, + @NonNull Activity secondaryActivity) { + assertSplitPair(primaryActivity, secondaryActivity, false /* matchParentBounds */); } /** Asserts that the two given activities are in split. */ private void assertSplitPair(@NonNull Activity primaryActivity, - @NonNull Activity secondaryActivity) { + @NonNull Activity secondaryActivity, boolean matchParentBounds) { assertSplitPair(mSplitController.getContainerWithActivity(primaryActivity), - mSplitController.getContainerWithActivity(secondaryActivity)); + mSplitController.getContainerWithActivity(secondaryActivity), matchParentBounds); } - /** Asserts that the two given TaskFragments are in split. */ private void assertSplitPair(@NonNull TaskFragmentContainer primaryContainer, @NonNull TaskFragmentContainer secondaryContainer) { + assertSplitPair(primaryContainer, secondaryContainer, false /* matchParentBounds*/); + } + + /** Asserts that the two given TaskFragments are in split. */ + private void assertSplitPair(@NonNull TaskFragmentContainer primaryContainer, + @NonNull TaskFragmentContainer secondaryContainer, boolean matchParentBounds) { assertNotNull(primaryContainer); assertNotNull(secondaryContainer); assertNotNull(mSplitController.getActiveSplitForContainers(primaryContainer, secondaryContainer)); if (primaryContainer.mInfo != null) { - assertTrue(primaryContainer.areLastRequestedBoundsEqual( - getSplitBounds(true /* isPrimary */))); + final Rect primaryBounds = matchParentBounds ? new Rect() + : getSplitBounds(true /* isPrimary */); + assertTrue(primaryContainer.areLastRequestedBoundsEqual(primaryBounds)); assertTrue(primaryContainer.isLastRequestedWindowingModeEqual( WINDOWING_MODE_MULTI_WINDOW)); } if (secondaryContainer.mInfo != null) { - assertTrue(secondaryContainer.areLastRequestedBoundsEqual( - getSplitBounds(false /* isPrimary */))); + final Rect secondaryBounds = matchParentBounds ? new Rect() + : getSplitBounds(false /* isPrimary */); + assertTrue(secondaryContainer.areLastRequestedBoundsEqual(secondaryBounds)); assertTrue(secondaryContainer.isLastRequestedWindowingModeEqual( WINDOWING_MODE_MULTI_WINDOW)); } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java index 906e9904566f..acc398a27baf 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java @@ -18,25 +18,44 @@ package androidx.window.extensions.embedding; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds; +import static androidx.window.extensions.embedding.SplitPresenter.POSITION_END; +import static androidx.window.extensions.embedding.SplitPresenter.POSITION_FILL; +import static androidx.window.extensions.embedding.SplitPresenter.POSITION_START; +import static androidx.window.extensions.embedding.SplitPresenter.getBoundsForPosition; +import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions; +import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSideBySide; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import android.app.Activity; +import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; +import android.util.Pair; +import android.util.Size; import android.window.TaskFragmentInfo; import android.window.WindowContainerTransaction; +import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -56,8 +75,6 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class SplitPresenterTest { - private static final int TASK_ID = 10; - private static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200); @Mock private Activity mActivity; @@ -77,11 +94,7 @@ public class SplitPresenterTest { mPresenter = mController.mPresenter; spyOn(mController); spyOn(mPresenter); - final Configuration activityConfig = new Configuration(); - activityConfig.windowConfiguration.setBounds(TASK_BOUNDS); - activityConfig.windowConfiguration.setMaxBounds(TASK_BOUNDS); - doReturn(mActivityResources).when(mActivity).getResources(); - doReturn(activityConfig).when(mActivityResources).getConfiguration(); + mActivity = createMockActivity(); } @Test @@ -129,4 +142,67 @@ public class SplitPresenterTest { verify(mTransaction, never()).setWindowingMode(any(), anyInt()); } + + @Test + public void testGetMinDimensionsForIntent() { + final Intent intent = new Intent(ApplicationProvider.getApplicationContext(), + MinimumDimensionActivity.class); + assertEquals(new Size(600, 1200), getMinDimensions(intent)); + } + + @Test + public void testShouldShowSideBySide() { + Activity secondaryActivity = createMockActivity(); + final SplitRule splitRule = createSplitRule(mActivity, secondaryActivity); + + assertTrue(shouldShowSideBySide(TASK_BOUNDS, splitRule)); + + // Set minDimensions of primary container to larger than primary bounds. + final Rect primaryBounds = getSplitBounds(true /* isPrimary */); + Pair<Size, Size> minDimensionsPair = new Pair<>( + new Size(primaryBounds.width() + 1, primaryBounds.height() + 1), null); + + assertFalse(shouldShowSideBySide(TASK_BOUNDS, splitRule, minDimensionsPair)); + } + + @Test + public void testGetBoundsForPosition() { + Activity secondaryActivity = createMockActivity(); + final SplitRule splitRule = createSplitRule(mActivity, secondaryActivity); + final Rect primaryBounds = getSplitBounds(true /* isPrimary */); + final Rect secondaryBounds = getSplitBounds(false /* isPrimary */); + + assertEquals("Primary bounds must be reported.", + primaryBounds, + getBoundsForPosition(POSITION_START, TASK_BOUNDS, splitRule, + mActivity, null /* miniDimensionsPair */)); + + assertEquals("Secondary bounds must be reported.", + secondaryBounds, + getBoundsForPosition(POSITION_END, TASK_BOUNDS, splitRule, + mActivity, null /* miniDimensionsPair */)); + assertEquals("Task bounds must be reported.", + new Rect(), + getBoundsForPosition(POSITION_FILL, TASK_BOUNDS, splitRule, + mActivity, null /* miniDimensionsPair */)); + + Pair<Size, Size> minDimensionsPair = new Pair<>( + new Size(primaryBounds.width() + 1, primaryBounds.height() + 1), null); + + assertEquals("Fullscreen bounds must be reported because of min dimensions.", + new Rect(), + getBoundsForPosition(POSITION_START, TASK_BOUNDS, + splitRule, mActivity, minDimensionsPair)); + } + + private Activity createMockActivity() { + final Activity activity = mock(Activity.class); + final Configuration activityConfig = new Configuration(); + activityConfig.windowConfiguration.setBounds(TASK_BOUNDS); + activityConfig.windowConfiguration.setMaxBounds(TASK_BOUNDS); + doReturn(mActivityResources).when(activity).getResources(); + doReturn(activityConfig).when(mActivityResources).getConfiguration(); + doReturn(new ActivityInfo()).when(activity).getActivityInfo(); + return activity; + } } diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index ebe202db4e54..dd67e48ef353 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -22,6 +22,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; @@ -53,9 +56,6 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class TaskContainerTest { - private static final int TASK_ID = 10; - private static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200); - @Mock private SplitController mController; diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java index af3ad70c04db..d31342bfb309 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java @@ -16,6 +16,8 @@ package androidx.window.extensions.embedding; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.mockito.ArgumentMatchers.anyInt; @@ -43,8 +45,6 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidJUnit4.class) public class TaskFragmentAnimationControllerTest { - private static final int TASK_ID = 10; - @Mock private TaskFragmentOrganizer mOrganizer; private TaskFragmentAnimationController mAnimationController; diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java index fcbd8a3ac020..28c2773e25cb 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java @@ -16,6 +16,9 @@ package androidx.window.extensions.embedding; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID; +import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertEquals; @@ -30,17 +33,13 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; -import android.annotation.NonNull; import android.app.Activity; import android.content.Intent; -import android.content.res.Configuration; -import android.graphics.Point; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.window.TaskFragmentInfo; -import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -55,7 +54,6 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; -import java.util.Collections; import java.util.List; /** @@ -68,8 +66,6 @@ import java.util.List; @SmallTest @RunWith(AndroidJUnit4.class) public class TaskFragmentContainerTest { - private static final int TASK_ID = 10; - @Mock private SplitPresenter mPresenter; @Mock @@ -311,18 +307,4 @@ public class TaskFragmentContainerTest { doReturn(activity).when(mController).getActivity(activityToken); return activity; } - - /** Creates a mock TaskFragmentInfo for the given TaskFragment. */ - private TaskFragmentInfo createMockTaskFragmentInfo(@NonNull TaskFragmentContainer container, - @NonNull Activity activity) { - return new TaskFragmentInfo(container.getTaskFragmentToken(), - mock(WindowContainerToken.class), - new Configuration(), - 1, - true /* isVisible */, - Collections.singletonList(activity.getActivityToken()), - new Point(), - false /* isTaskClearedForReuse */, - false /* isTaskFragmentClearedForPip */); - } } diff --git a/libs/WindowManager/Shell/res/color/taskbar_background.xml b/libs/WindowManager/Shell/res/color/taskbar_background.xml index 329e5b9b31a0..b3d260299106 100644 --- a/libs/WindowManager/Shell/res/color/taskbar_background.xml +++ b/libs/WindowManager/Shell/res/color/taskbar_background.xml @@ -14,6 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> +<!-- Should be the same as in packages/apps/Launcher3/res/color-v31/taskbar_background.xml --> <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="@android:color/system_neutral1_500" android:lStar="35" /> + <item android:color="@android:color/system_neutral1_500" android:lStar="15" /> </selector>
\ No newline at end of file diff --git a/libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml b/libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml index cb516cdbe49b..df5985c605d1 100644 --- a/libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml +++ b/libs/WindowManager/Shell/res/layout/bubble_overflow_container.xml @@ -30,7 +30,8 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center_horizontal" - android:gravity="center"/> + android:gravity="center" + android:clipChildren="false"/> <LinearLayout android:id="@+id/bubble_overflow_empty_state" diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java index 06f4367752fb..c5f7c19518a7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellCommandHandlerImpl.java @@ -18,11 +18,9 @@ package com.android.wm.shell; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; -import com.android.wm.shell.apppairs.AppPairsController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasksController; @@ -39,12 +37,10 @@ import java.util.Optional; public final class ShellCommandHandlerImpl { private static final String TAG = ShellCommandHandlerImpl.class.getSimpleName(); - private final Optional<LegacySplitScreenController> mLegacySplitScreenOptional; private final Optional<SplitScreenController> mSplitScreenOptional; private final Optional<Pip> mPipOptional; private final Optional<OneHandedController> mOneHandedOptional; private final Optional<HideDisplayCutoutController> mHideDisplayCutout; - private final Optional<AppPairsController> mAppPairsOptional; private final Optional<RecentTasksController> mRecentTasks; private final ShellTaskOrganizer mShellTaskOrganizer; private final KidsModeTaskOrganizer mKidsModeTaskOrganizer; @@ -54,23 +50,19 @@ public final class ShellCommandHandlerImpl { public ShellCommandHandlerImpl( ShellTaskOrganizer shellTaskOrganizer, KidsModeTaskOrganizer kidsModeTaskOrganizer, - Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<Pip> pipOptional, Optional<OneHandedController> oneHandedOptional, Optional<HideDisplayCutoutController> hideDisplayCutout, - Optional<AppPairsController> appPairsOptional, Optional<RecentTasksController> recentTasks, ShellExecutor mainExecutor) { mShellTaskOrganizer = shellTaskOrganizer; mKidsModeTaskOrganizer = kidsModeTaskOrganizer; mRecentTasks = recentTasks; - mLegacySplitScreenOptional = legacySplitScreenOptional; mSplitScreenOptional = splitScreenOptional; mPipOptional = pipOptional; mOneHandedOptional = oneHandedOptional; mHideDisplayCutout = hideDisplayCutout; - mAppPairsOptional = appPairsOptional; mMainExecutor = mainExecutor; } @@ -84,14 +76,10 @@ public final class ShellCommandHandlerImpl { pw.println(); pw.println(); mPipOptional.ifPresent(pip -> pip.dump(pw)); - mLegacySplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw)); mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw)); mHideDisplayCutout.ifPresent(hideDisplayCutout -> hideDisplayCutout.dump(pw)); pw.println(); pw.println(); - mAppPairsOptional.ifPresent(appPairs -> appPairs.dump(pw, "")); - pw.println(); - pw.println(); mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw, "")); pw.println(); pw.println(); @@ -109,10 +97,6 @@ public final class ShellCommandHandlerImpl { return false; } switch (args[1]) { - case "pair": - return runPair(args, pw); - case "unpair": - return runUnpair(args, pw); case "moveToSideStage": return runMoveToSideStage(args, pw); case "removeFromSideStage": @@ -126,29 +110,6 @@ public final class ShellCommandHandlerImpl { } } - private boolean runPair(String[] args, PrintWriter pw) { - if (args.length < 4) { - // First two arguments are "WMShell" and command name. - pw.println("Error: two task ids should be provided as arguments"); - return false; - } - final int taskId1 = new Integer(args[2]); - final int taskId2 = new Integer(args[3]); - mAppPairsOptional.ifPresent(appPairs -> appPairs.pair(taskId1, taskId2)); - return true; - } - - private boolean runUnpair(String[] args, PrintWriter pw) { - if (args.length < 3) { - // First two arguments are "WMShell" and command name. - pw.println("Error: task id should be provided as an argument"); - return false; - } - final int taskId = new Integer(args[2]); - mAppPairsOptional.ifPresent(appPairs -> appPairs.unpair(taskId)); - return true; - } - private boolean runMoveToSideStage(String[] args, PrintWriter pw) { if (args.length < 3) { // First arguments are "WMShell" and command name. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java index 62fb840d29d1..f6a3e7fb54d9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -18,7 +18,6 @@ package com.android.wm.shell; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; -import com.android.wm.shell.apppairs.AppPairsController; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -34,6 +33,7 @@ import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.startingsurface.StartingWindowController; +import com.android.wm.shell.transition.DefaultMixedHandler; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.UnfoldTransitionHandler; @@ -53,7 +53,6 @@ public class ShellInitImpl { private final KidsModeTaskOrganizer mKidsModeTaskOrganizer; private final Optional<BubbleController> mBubblesOptional; private final Optional<SplitScreenController> mSplitScreenOptional; - private final Optional<AppPairsController> mAppPairsOptional; private final Optional<PipTouchHandler> mPipTouchHandlerOptional; private final FullscreenTaskListener mFullscreenTaskListener; private final Optional<FullscreenUnfoldController> mFullscreenUnfoldController; @@ -75,7 +74,6 @@ public class ShellInitImpl { KidsModeTaskOrganizer kidsModeTaskOrganizer, Optional<BubbleController> bubblesOptional, Optional<SplitScreenController> splitScreenOptional, - Optional<AppPairsController> appPairsOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController, @@ -93,7 +91,6 @@ public class ShellInitImpl { mKidsModeTaskOrganizer = kidsModeTaskOrganizer; mBubblesOptional = bubblesOptional; mSplitScreenOptional = splitScreenOptional; - mAppPairsOptional = appPairsOptional; mFullscreenTaskListener = fullscreenTaskListener; mPipTouchHandlerOptional = pipTouchHandlerOptional; mFullscreenUnfoldController = fullscreenUnfoldTransitionController; @@ -121,7 +118,6 @@ public class ShellInitImpl { mShellTaskOrganizer.initStartingWindow(mStartingWindow); mShellTaskOrganizer.registerOrganizer(); - mAppPairsOptional.ifPresent(AppPairsController::onOrganizerRegistered); mSplitScreenOptional.ifPresent(SplitScreenController::onOrganizerRegistered); mBubblesOptional.ifPresent(BubbleController::initialize); @@ -131,6 +127,13 @@ public class ShellInitImpl { if (Transitions.ENABLE_SHELL_TRANSITIONS) { mTransitions.register(mShellTaskOrganizer); mUnfoldTransitionHandler.ifPresent(UnfoldTransitionHandler::init); + if (mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) { + final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions, + mPipTouchHandlerOptional.get().getTransitionHandler(), + mSplitScreenOptional.get().getTransitionHandler()); + // Added at end so that it has highest priority. + mTransitions.addHandler(mixedHandler); + } } // TODO(b/181599115): This should really be the pip controller, but until we can provide the diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java index 2aead9392e59..a0dde6ad168d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/Interpolators.java @@ -53,6 +53,19 @@ public class Interpolators { public static final Interpolator LINEAR_OUT_SLOW_IN = new PathInterpolator(0f, 0f, 0.2f, 1f); /** + * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that + * is disappearing e.g. when moving off screen. + */ + public static final Interpolator EMPHASIZED_ACCELERATE = new PathInterpolator( + 0.3f, 0f, 0.8f, 0.15f); + + /** + * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that + * is appearing e.g. when coming from off screen + */ + public static final Interpolator EMPHASIZED_DECELERATE = new PathInterpolator( + 0.05f, 0.7f, 0.1f, 1f); + /** * Interpolator to be used when animating a move based on a click. Pair with enough duration. */ public static final Interpolator TOUCH_RESPONSE = new PathInterpolator(0.3f, 0f, 0.1f, 1f); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java deleted file mode 100644 index 3f0b01bef0ce..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.apppairs; - -import static android.app.ActivityTaskManager.INVALID_TASK_ID; -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; - -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; - -import android.app.ActivityManager; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.protolog.common.ProtoLog; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.SurfaceUtils; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.split.SplitLayout; -import com.android.wm.shell.common.split.SplitWindowManager; - -import java.io.PrintWriter; - -/** - * An app-pairs consisting of {@link #mRootTaskInfo} that acts as the hierarchy parent of - * {@link #mTaskInfo1} and {@link #mTaskInfo2} in the pair. - * Also includes all UI for managing the pair like the divider. - */ -class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayoutHandler { - private static final String TAG = AppPair.class.getSimpleName(); - - private ActivityManager.RunningTaskInfo mRootTaskInfo; - private SurfaceControl mRootTaskLeash; - private ActivityManager.RunningTaskInfo mTaskInfo1; - private SurfaceControl mTaskLeash1; - private ActivityManager.RunningTaskInfo mTaskInfo2; - private SurfaceControl mTaskLeash2; - private SurfaceControl mDimLayer1; - private SurfaceControl mDimLayer2; - private final SurfaceSession mSurfaceSession = new SurfaceSession(); - - private final AppPairsController mController; - private final SyncTransactionQueue mSyncQueue; - private final DisplayController mDisplayController; - private final DisplayImeController mDisplayImeController; - private final DisplayInsetsController mDisplayInsetsController; - private SplitLayout mSplitLayout; - - private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks = - new SplitWindowManager.ParentContainerCallbacks() { - @Override - public void attachToParentSurface(SurfaceControl.Builder b) { - b.setParent(mRootTaskLeash); - } - - @Override - public void onLeashReady(SurfaceControl leash) { - mSyncQueue.runInSync(t -> t - .show(leash) - .setLayer(leash, Integer.MAX_VALUE) - .setPosition(leash, - mSplitLayout.getDividerBounds().left, - mSplitLayout.getDividerBounds().top)); - } - }; - - AppPair(AppPairsController controller) { - mController = controller; - mSyncQueue = controller.getSyncTransactionQueue(); - mDisplayController = controller.getDisplayController(); - mDisplayImeController = controller.getDisplayImeController(); - mDisplayInsetsController = controller.getDisplayInsetsController(); - } - - int getRootTaskId() { - return mRootTaskInfo != null ? mRootTaskInfo.taskId : INVALID_TASK_ID; - } - - private int getTaskId1() { - return mTaskInfo1 != null ? mTaskInfo1.taskId : INVALID_TASK_ID; - } - - private int getTaskId2() { - return mTaskInfo2 != null ? mTaskInfo2.taskId : INVALID_TASK_ID; - } - - boolean contains(int taskId) { - return taskId == getRootTaskId() || taskId == getTaskId1() || taskId == getTaskId2(); - } - - boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) { - ProtoLog.v(WM_SHELL_TASK_ORG, "pair task1=%d task2=%d in AppPair=%s", - task1.taskId, task2.taskId, this); - - if (!task1.supportsMultiWindow || !task2.supportsMultiWindow) { - ProtoLog.e(WM_SHELL_TASK_ORG, - "Can't pair tasks that doesn't support multi window, " - + "task1.supportsMultiWindow=%b, task2.supportsMultiWindow=%b", - task1.supportsMultiWindow, task2.supportsMultiWindow); - return false; - } - - mTaskInfo1 = task1; - mTaskInfo2 = task2; - mSplitLayout = new SplitLayout(TAG + "SplitDivider", - mDisplayController.getDisplayContext(mRootTaskInfo.displayId), - mRootTaskInfo.configuration, this /* layoutChangeListener */, - mParentContainerCallbacks, mDisplayImeController, mController.getTaskOrganizer(), - SplitLayout.PARALLAX_DISMISSING); - mDisplayInsetsController.addInsetsChangedListener(mRootTaskInfo.displayId, mSplitLayout); - - final WindowContainerToken token1 = task1.token; - final WindowContainerToken token2 = task2.token; - final WindowContainerTransaction wct = new WindowContainerTransaction(); - - wct.setHidden(mRootTaskInfo.token, false) - .reparent(token1, mRootTaskInfo.token, true /* onTop */) - .reparent(token2, mRootTaskInfo.token, true /* onTop */) - .setWindowingMode(token1, WINDOWING_MODE_MULTI_WINDOW) - .setWindowingMode(token2, WINDOWING_MODE_MULTI_WINDOW) - .setBounds(token1, mSplitLayout.getBounds1()) - .setBounds(token2, mSplitLayout.getBounds2()) - // Moving the root task to top after the child tasks were repareted , or the root - // task cannot be visible and focused. - .reorder(mRootTaskInfo.token, true); - mController.getTaskOrganizer().applyTransaction(wct); - return true; - } - - void unpair() { - unpair(null /* toTopToken */); - } - - private void unpair(@Nullable WindowContainerToken toTopToken) { - final WindowContainerToken token1 = mTaskInfo1.token; - final WindowContainerToken token2 = mTaskInfo2.token; - final WindowContainerTransaction wct = new WindowContainerTransaction(); - - // Reparent out of this container and reset windowing mode. - wct.setHidden(mRootTaskInfo.token, true) - .reorder(mRootTaskInfo.token, false) - .reparent(token1, null, token1 == toTopToken /* onTop */) - .reparent(token2, null, token2 == toTopToken /* onTop */) - .setWindowingMode(token1, WINDOWING_MODE_UNDEFINED) - .setWindowingMode(token2, WINDOWING_MODE_UNDEFINED); - mController.getTaskOrganizer().applyTransaction(wct); - - mTaskInfo1 = null; - mTaskInfo2 = null; - mSplitLayout.release(); - mSplitLayout = null; - } - - @Override - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - if (mRootTaskInfo == null || taskInfo.taskId == mRootTaskInfo.taskId) { - mRootTaskInfo = taskInfo; - mRootTaskLeash = leash; - } else if (taskInfo.taskId == getTaskId1()) { - mTaskInfo1 = taskInfo; - mTaskLeash1 = leash; - mSyncQueue.runInSync(t -> mDimLayer1 = - SurfaceUtils.makeDimLayer(t, mTaskLeash1, "Dim layer", mSurfaceSession)); - } else if (taskInfo.taskId == getTaskId2()) { - mTaskInfo2 = taskInfo; - mTaskLeash2 = leash; - mSyncQueue.runInSync(t -> mDimLayer2 = - SurfaceUtils.makeDimLayer(t, mTaskLeash2, "Dim layer", mSurfaceSession)); - } else { - throw new IllegalStateException("Unknown task=" + taskInfo.taskId); - } - - if (mTaskLeash1 == null || mTaskLeash2 == null) return; - - mSplitLayout.init(); - - mSyncQueue.runInSync(t -> t - .show(mRootTaskLeash) - .show(mTaskLeash1) - .show(mTaskLeash2) - .setPosition(mTaskLeash1, - mTaskInfo1.positionInParent.x, - mTaskInfo1.positionInParent.y) - .setPosition(mTaskLeash2, - mTaskInfo2.positionInParent.x, - mTaskInfo2.positionInParent.y)); - } - - @Override - public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - if (!taskInfo.supportsMultiWindow) { - // Dismiss AppPair if the task no longer supports multi window. - mController.unpair(mRootTaskInfo.taskId); - return; - } - if (taskInfo.taskId == getRootTaskId()) { - if (mRootTaskInfo.isVisible != taskInfo.isVisible) { - mSyncQueue.runInSync(t -> { - if (taskInfo.isVisible) { - t.show(mRootTaskLeash); - } else { - t.hide(mRootTaskLeash); - } - }); - } - mRootTaskInfo = taskInfo; - - if (mSplitLayout != null - && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) { - onLayoutSizeChanged(mSplitLayout); - } - } else if (taskInfo.taskId == getTaskId1()) { - mTaskInfo1 = taskInfo; - } else if (taskInfo.taskId == getTaskId2()) { - mTaskInfo2 = taskInfo; - } else { - throw new IllegalStateException("Unknown task=" + taskInfo.taskId); - } - } - - @Override - public int getSplitItemPosition(WindowContainerToken token) { - if (token == null) { - return SPLIT_POSITION_UNDEFINED; - } - - if (token.equals(mTaskInfo1.getToken())) { - return SPLIT_POSITION_TOP_OR_LEFT; - } else if (token.equals(mTaskInfo2.getToken())) { - return SPLIT_POSITION_BOTTOM_OR_RIGHT; - } - - return SPLIT_POSITION_UNDEFINED; - } - - @Override - public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - if (taskInfo.taskId == getRootTaskId()) { - // We don't want to release this object back to the pool since the root task went away. - mController.unpair(mRootTaskInfo.taskId, false /* releaseToPool */); - } else if (taskInfo.taskId == getTaskId1()) { - mController.unpair(mRootTaskInfo.taskId); - mSyncQueue.runInSync(t -> t.remove(mDimLayer1)); - } else if (taskInfo.taskId == getTaskId2()) { - mController.unpair(mRootTaskInfo.taskId); - mSyncQueue.runInSync(t -> t.remove(mDimLayer2)); - } - } - - @Override - public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { - b.setParent(findTaskSurface(taskId)); - } - - @Override - public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, - SurfaceControl.Transaction t) { - t.reparent(sc, findTaskSurface(taskId)); - } - - private SurfaceControl findTaskSurface(int taskId) { - if (getRootTaskId() == taskId) { - return mRootTaskLeash; - } else if (getTaskId1() == taskId) { - return mTaskLeash1; - } else if (getTaskId2() == taskId) { - return mTaskLeash2; - } else { - throw new IllegalArgumentException("There is no surface for taskId=" + taskId); - } - } - - @Override - public void dump(@NonNull PrintWriter pw, String prefix) { - final String innerPrefix = prefix + " "; - final String childPrefix = innerPrefix + " "; - pw.println(prefix + this); - if (mRootTaskInfo != null) { - pw.println(innerPrefix + "Root taskId=" + mRootTaskInfo.taskId - + " winMode=" + mRootTaskInfo.getWindowingMode()); - } - if (mTaskInfo1 != null) { - pw.println(innerPrefix + "1 taskId=" + mTaskInfo1.taskId - + " winMode=" + mTaskInfo1.getWindowingMode()); - } - if (mTaskInfo2 != null) { - pw.println(innerPrefix + "2 taskId=" + mTaskInfo2.taskId - + " winMode=" + mTaskInfo2.getWindowingMode()); - } - } - - @Override - public String toString() { - return TAG + "#" + getRootTaskId(); - } - - @Override - public void onSnappedToDismiss(boolean snappedToEnd) { - unpair(snappedToEnd ? mTaskInfo1.token : mTaskInfo2.token /* toTopToken */); - } - - @Override - public void onLayoutPositionChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> - layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2, - true /* applyResizingOffset */)); - } - - @Override - public void onLayoutSizeChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> - layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2, - true /* applyResizingOffset */)); - } - - @Override - public void onLayoutSizeChanged(SplitLayout layout) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - layout.applyTaskChanges(wct, mTaskInfo1, mTaskInfo2); - mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> - layout.applySurfaceChanges(t, mTaskLeash1, mTaskLeash2, mDimLayer1, mDimLayer2, - false /* applyResizingOffset */)); - } - - @Override - public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - layout.applyLayoutOffsetTarget(wct, offsetX, offsetY, mTaskInfo1, mTaskInfo2); - mController.getTaskOrganizer().applyTransaction(wct); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java deleted file mode 100644 index a9b1dbc3c23b..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairs.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.apppairs; - -import android.app.ActivityManager; - -import androidx.annotation.NonNull; - -import com.android.wm.shell.common.annotations.ExternalThread; - -import java.io.PrintWriter; - -/** - * Interface to engage app pairs feature. - */ -@ExternalThread -public interface AppPairs { - /** Pairs indicated tasks. */ - boolean pair(int task1, int task2); - /** Pairs indicated tasks. */ - boolean pair(ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2); - /** Unpairs any app-pair containing this task id. */ - void unpair(int taskId); -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java deleted file mode 100644 index 53234ab971d6..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsController.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.apppairs; - -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; - -import android.app.ActivityManager; -import android.util.Slog; -import android.util.SparseArray; - -import androidx.annotation.NonNull; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.SyncTransactionQueue; - -import java.io.PrintWriter; - -/** - * Class manages app-pairs multitasking mode and implements the main interface {@link AppPairs}. - */ -public class AppPairsController { - private static final String TAG = AppPairsController.class.getSimpleName(); - - private final ShellTaskOrganizer mTaskOrganizer; - private final SyncTransactionQueue mSyncQueue; - private final ShellExecutor mMainExecutor; - private final AppPairsImpl mImpl = new AppPairsImpl(); - - private AppPairsPool mPairsPool; - // Active app-pairs mapped by root task id key. - private final SparseArray<AppPair> mActiveAppPairs = new SparseArray<>(); - private final DisplayController mDisplayController; - private final DisplayImeController mDisplayImeController; - private final DisplayInsetsController mDisplayInsetsController; - - public AppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, - DisplayController displayController, ShellExecutor mainExecutor, - DisplayImeController displayImeController, - DisplayInsetsController displayInsetsController) { - mTaskOrganizer = organizer; - mSyncQueue = syncQueue; - mDisplayController = displayController; - mDisplayImeController = displayImeController; - mDisplayInsetsController = displayInsetsController; - mMainExecutor = mainExecutor; - } - - public AppPairs asAppPairs() { - return mImpl; - } - - public void onOrganizerRegistered() { - if (mPairsPool == null) { - setPairsPool(new AppPairsPool(this)); - } - } - - @VisibleForTesting - public void setPairsPool(AppPairsPool pool) { - mPairsPool = pool; - } - - public boolean pair(int taskId1, int taskId2) { - final ActivityManager.RunningTaskInfo task1 = mTaskOrganizer.getRunningTaskInfo(taskId1); - final ActivityManager.RunningTaskInfo task2 = mTaskOrganizer.getRunningTaskInfo(taskId2); - if (task1 == null || task2 == null) { - return false; - } - return pair(task1, task2); - } - - public boolean pair(ActivityManager.RunningTaskInfo task1, - ActivityManager.RunningTaskInfo task2) { - return pairInner(task1, task2) != null; - } - - @VisibleForTesting - public AppPair pairInner( - @NonNull ActivityManager.RunningTaskInfo task1, - @NonNull ActivityManager.RunningTaskInfo task2) { - final AppPair pair = mPairsPool.acquire(); - if (!pair.pair(task1, task2)) { - mPairsPool.release(pair); - return null; - } - - mActiveAppPairs.put(pair.getRootTaskId(), pair); - return pair; - } - - public void unpair(int taskId) { - unpair(taskId, true /* releaseToPool */); - } - - public void unpair(int taskId, boolean releaseToPool) { - AppPair pair = mActiveAppPairs.get(taskId); - if (pair == null) { - for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) { - final AppPair candidate = mActiveAppPairs.valueAt(i); - if (candidate.contains(taskId)) { - pair = candidate; - break; - } - } - } - if (pair == null) { - ProtoLog.v(WM_SHELL_TASK_ORG, "taskId %d isn't isn't in an app-pair.", taskId); - return; - } - - ProtoLog.v(WM_SHELL_TASK_ORG, "unpair taskId=%d pair=%s", taskId, pair); - mActiveAppPairs.remove(pair.getRootTaskId()); - pair.unpair(); - if (releaseToPool) { - mPairsPool.release(pair); - } - } - - ShellTaskOrganizer getTaskOrganizer() { - return mTaskOrganizer; - } - - SyncTransactionQueue getSyncTransactionQueue() { - return mSyncQueue; - } - - DisplayController getDisplayController() { - return mDisplayController; - } - - DisplayImeController getDisplayImeController() { - return mDisplayImeController; - } - - DisplayInsetsController getDisplayInsetsController() { - return mDisplayInsetsController; - } - - public void dump(@NonNull PrintWriter pw, String prefix) { - final String innerPrefix = prefix + " "; - final String childPrefix = innerPrefix + " "; - pw.println(prefix + this); - - for (int i = mActiveAppPairs.size() - 1; i >= 0; --i) { - mActiveAppPairs.valueAt(i).dump(pw, childPrefix); - } - - if (mPairsPool != null) { - mPairsPool.dump(pw, prefix); - } - } - - @Override - public String toString() { - return TAG + "#" + mActiveAppPairs.size(); - } - - private class AppPairsImpl implements AppPairs { - @Override - public boolean pair(int task1, int task2) { - boolean[] result = new boolean[1]; - try { - mMainExecutor.executeBlocking(() -> { - result[0] = AppPairsController.this.pair(task1, task2); - }); - } catch (InterruptedException e) { - Slog.e(TAG, "Failed to pair tasks: " + task1 + ", " + task2); - } - return result[0]; - } - - @Override - public boolean pair(ActivityManager.RunningTaskInfo task1, - ActivityManager.RunningTaskInfo task2) { - boolean[] result = new boolean[1]; - try { - mMainExecutor.executeBlocking(() -> { - result[0] = AppPairsController.this.pair(task1, task2); - }); - } catch (InterruptedException e) { - Slog.e(TAG, "Failed to pair tasks: " + task1 + ", " + task2); - } - return result[0]; - } - - @Override - public void unpair(int taskId) { - mMainExecutor.execute(() -> { - AppPairsController.this.unpair(taskId); - }); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java deleted file mode 100644 index 5c6037ea0702..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPairsPool.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.apppairs; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.view.Display.DEFAULT_DISPLAY; - -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; - -import androidx.annotation.NonNull; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.protolog.common.ProtoLog; - -import java.io.PrintWriter; -import java.util.ArrayList; - -/** - * Class that manager pool of {@link AppPair} objects. Helps reduce the need to call system_server - * to create a root task for the app-pair when needed since we always have one ready to go. - */ -class AppPairsPool { - private static final String TAG = AppPairsPool.class.getSimpleName(); - - @VisibleForTesting - final AppPairsController mController; - // The pool - private final ArrayList<AppPair> mPool = new ArrayList(); - - AppPairsPool(AppPairsController controller) { - mController = controller; - incrementPool(); - } - - AppPair acquire() { - final AppPair entry = mPool.remove(mPool.size() - 1); - ProtoLog.v(WM_SHELL_TASK_ORG, "acquire entry.taskId=%s listener=%s size=%d", - entry.getRootTaskId(), entry, mPool.size()); - if (mPool.size() == 0) { - incrementPool(); - } - return entry; - } - - void release(AppPair entry) { - mPool.add(entry); - ProtoLog.v(WM_SHELL_TASK_ORG, "release entry.taskId=%s listener=%s size=%d", - entry.getRootTaskId(), entry, mPool.size()); - } - - @VisibleForTesting - void incrementPool() { - ProtoLog.v(WM_SHELL_TASK_ORG, "incrementPool size=%d", mPool.size()); - final AppPair entry = new AppPair(mController); - // TODO: multi-display... - mController.getTaskOrganizer().createRootTask( - DEFAULT_DISPLAY, WINDOWING_MODE_FULLSCREEN, entry); - mPool.add(entry); - } - - @VisibleForTesting - int poolSize() { - return mPool.size(); - } - - public void dump(@NonNull PrintWriter pw, String prefix) { - final String innerPrefix = prefix + " "; - final String childPrefix = innerPrefix + " "; - pw.println(prefix + this); - for (int i = mPool.size() - 1; i >= 0; --i) { - mPool.get(i).dump(pw, childPrefix); - } - } - - @Override - public String toString() { - return TAG + "#" + mPool.size(); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS deleted file mode 100644 index 4d9b520e3f0e..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# WM shell sub-modules apppair owner -chenghsiuchang@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index f427a2c4bc95..ea3712ba34b4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -25,6 +25,7 @@ import static android.view.View.VISIBLE; import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_CONTROLLER; +import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.wm.shell.bubbles.BubblePositioner.TASKBAR_POSITION_BOTTOM; @@ -79,7 +80,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; import android.view.WindowManager; -import android.window.WindowContainerTransaction; import androidx.annotation.MainThread; import androidx.annotation.Nullable; @@ -90,7 +90,6 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TaskViewTransitions; import com.android.wm.shell.WindowManagerShellWrapper; -import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; @@ -435,17 +434,13 @@ public class BubbleController { }); mDisplayController.addDisplayChangingController( - new DisplayChangeController.OnDisplayChangingListener() { - @Override - public void onRotateDisplay(int displayId, int fromRotation, int toRotation, - WindowContainerTransaction t) { - // This is triggered right before the rotation is applied - if (fromRotation != toRotation) { - if (mStackView != null) { - // Layout listener set on stackView will update the positioner - // once the rotation is applied - mStackView.onOrientationChanged(); - } + (displayId, fromRotation, toRotation, newDisplayAreaInfo, t) -> { + // This is triggered right before the rotation is applied + if (fromRotation != toRotation) { + if (mStackView != null) { + // Layout listener set on stackView will update the positioner + // once the rotation is applied + mStackView.onOrientationChanged(); } } }); @@ -885,6 +880,19 @@ public class BubbleController { } } + private void onNotificationPanelExpandedChanged(boolean expanded) { + if (DEBUG_BUBBLE_GESTURE) { + Log.d(TAG, "onNotificationPanelExpandedChanged: expanded=" + expanded); + } + if (mStackView != null && mStackView.isExpanded()) { + if (expanded) { + mStackView.stopMonitoringSwipeUpGesture(); + } else { + mStackView.startMonitoringSwipeUpGesture(); + } + } + } + private void setSysuiProxy(Bubbles.SysuiProxy proxy) { mSysuiProxy = proxy; } @@ -1468,6 +1476,18 @@ public class BubbleController { } /** + * Check if notification panel is in an expanded state. + * Makes a call to System UI process and delivers the result via {@code callback} on the + * WM Shell main thread. + * + * @param callback callback that has the result of notification panel expanded state + */ + public void isNotificationPanelExpanded(Consumer<Boolean> callback) { + mSysuiProxy.isNotificationPanelExpand(expanded -> + mMainExecutor.execute(() -> callback.accept(expanded))); + } + + /** * Description of current bubble state. */ private void dump(PrintWriter pw, String[] args) { @@ -1546,7 +1566,7 @@ public class BubbleController { public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) { mBubblePositioner.setImeVisible(imeVisible, imeHeight); if (mStackView != null) { - mStackView.animateForIme(imeVisible); + mStackView.setImeVisible(imeVisible); } } } @@ -1843,6 +1863,12 @@ public class BubbleController { } @Override + public void onNotificationPanelExpandedChanged(boolean expanded) { + mMainExecutor.execute( + () -> BubbleController.this.onNotificationPanelExpandedChanged(expanded)); + } + + @Override public void dump(PrintWriter pw, String[] args) { try { mMainExecutor.executeBlocking(() -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java index dc2ace949f0c..dce6b56261ff 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleDebugConfig.java @@ -46,6 +46,9 @@ public class BubbleDebugConfig { static final boolean DEBUG_OVERFLOW = false; static final boolean DEBUG_USER_EDUCATION = false; static final boolean DEBUG_POSITIONER = false; + public static final boolean DEBUG_COLLAPSE_ANIMATOR = false; + static final boolean DEBUG_BUBBLE_GESTURE = false; + public static boolean DEBUG_EXPANDED_VIEW_DRAGGING = false; private static final boolean FORCE_SHOW_USER_EDUCATION = false; private static final String FORCE_SHOW_USER_EDUCATION_SETTING = diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java index b8bf1a8e497e..5df7b8a578d6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java @@ -43,10 +43,13 @@ import android.graphics.CornerPathEffect; import android.graphics.Outline; import android.graphics.Paint; import android.graphics.Picture; +import android.graphics.PointF; import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.os.RemoteException; import android.util.AttributeSet; +import android.util.FloatProperty; +import android.util.IntProperty; import android.util.Log; import android.util.TypedValue; import android.view.LayoutInflater; @@ -75,6 +78,48 @@ import java.io.PrintWriter; public class BubbleExpandedView extends LinearLayout { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleExpandedView" : TAG_BUBBLES; + /** {@link IntProperty} for updating bottom clip */ + public static final IntProperty<BubbleExpandedView> BOTTOM_CLIP_PROPERTY = + new IntProperty<BubbleExpandedView>("bottomClip") { + @Override + public void setValue(BubbleExpandedView expandedView, int value) { + expandedView.setBottomClip(value); + } + + @Override + public Integer get(BubbleExpandedView expandedView) { + return expandedView.mBottomClip; + } + }; + + /** {@link FloatProperty} for updating taskView or overflow alpha */ + public static final FloatProperty<BubbleExpandedView> CONTENT_ALPHA = + new FloatProperty<BubbleExpandedView>("contentAlpha") { + @Override + public void setValue(BubbleExpandedView expandedView, float value) { + expandedView.setContentAlpha(value); + } + + @Override + public Float get(BubbleExpandedView expandedView) { + return expandedView.getContentAlpha(); + } + }; + + /** {@link FloatProperty} for updating manage button alpha */ + public static final FloatProperty<BubbleExpandedView> MANAGE_BUTTON_ALPHA = + new FloatProperty<BubbleExpandedView>("manageButtonAlpha") { + @Override + public void setValue(BubbleExpandedView expandedView, float value) { + expandedView.mManageButton.setAlpha(value); + } + + @Override + public Float get(BubbleExpandedView expandedView) { + return expandedView.mManageButton.getAlpha(); + } + }; + // The triangle pointing to the expanded view private View mPointerView; @Nullable private int[] mExpandedViewContainerLocation; @@ -90,7 +135,7 @@ public class BubbleExpandedView extends LinearLayout { /** * Whether we want the {@code TaskView}'s content to be visible (alpha = 1f). If - * {@link #mIsAlphaAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha + * {@link #mIsAnimating} is true, this may not reflect the {@code TaskView}'s actual alpha * value until the animation ends. */ private boolean mIsContentVisible = false; @@ -99,12 +144,13 @@ public class BubbleExpandedView extends LinearLayout { * Whether we're animating the {@code TaskView}'s alpha value. If so, we will hold off on * applying alpha changes from {@link #setContentVisibility} until the animation ends. */ - private boolean mIsAlphaAnimating = false; + private boolean mIsAnimating = false; private int mPointerWidth; private int mPointerHeight; private float mPointerRadius; private float mPointerOverlap; + private final PointF mPointerPos = new PointF(); private CornerPathEffect mPointerEffect; private ShapeDrawable mCurrentPointer; private ShapeDrawable mTopPointer; @@ -113,11 +159,13 @@ public class BubbleExpandedView extends LinearLayout { private float mCornerRadius = 0f; private int mBackgroundColorFloating; private boolean mUsingMaxHeight; - + private int mTopClip = 0; + private int mBottomClip = 0; @Nullable private Bubble mBubble; private PendingIntent mPendingIntent; // TODO(b/170891664): Don't use a flag, set the BubbleOverflow object instead private boolean mIsOverflow; + private boolean mIsClipping; private BubbleController mController; private BubbleStackView mStackView; @@ -268,7 +316,8 @@ public class BubbleExpandedView extends LinearLayout { mExpandedViewContainer.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { - outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius); + Rect clip = new Rect(0, mTopClip, view.getWidth(), view.getHeight() - mBottomClip); + outline.setRoundRect(clip, mCornerRadius); } }); mExpandedViewContainer.setClipToOutline(true); @@ -300,9 +349,9 @@ public class BubbleExpandedView extends LinearLayout { // they should not collapse the stack (which all other touches on areas around the AV // would do). if (motionEvent.getRawY() >= avBounds.top - && motionEvent.getRawY() <= avBounds.bottom - && (motionEvent.getRawX() < avBounds.left - || motionEvent.getRawX() > avBounds.right)) { + && motionEvent.getRawY() <= avBounds.bottom + && (motionEvent.getRawX() < avBounds.left + || motionEvent.getRawX() > avBounds.right)) { return true; } @@ -384,7 +433,7 @@ public class BubbleExpandedView extends LinearLayout { } void applyThemeAttrs() { - final TypedArray ta = mContext.obtainStyledAttributes(new int[] { + final TypedArray ta = mContext.obtainStyledAttributes(new int[]{ android.R.attr.dialogCornerRadius, android.R.attr.colorBackgroundFloating}); boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows( @@ -429,7 +478,7 @@ public class BubbleExpandedView extends LinearLayout { * ordering surfaces during animations. When content is drawn on top of the app (e.g. bubble * being dragged out, the manage menu) this is set to false, otherwise it should be true. */ - void setSurfaceZOrderedOnTop(boolean onTop) { + public void setSurfaceZOrderedOnTop(boolean onTop) { if (mTaskView == null) { return; } @@ -510,12 +559,12 @@ public class BubbleExpandedView extends LinearLayout { } /** - * Whether we are currently animating the {@code TaskView}'s alpha value. If this is set to + * Whether we are currently animating the {@code TaskView}. If this is set to * true, calls to {@link #setContentVisibility} will not be applied until this is set to false * again. */ - void setAlphaAnimating(boolean animating) { - mIsAlphaAnimating = animating; + public void setAnimating(boolean animating) { + mIsAnimating = animating; // If we're done animating, apply the correct if (!animating) { @@ -524,15 +573,128 @@ public class BubbleExpandedView extends LinearLayout { } /** - * Sets the alpha of the underlying {@code TaskView}, since changing the expanded view's alpha - * does not affect the {@code TaskView} since it uses a Surface. + * Get alpha from underlying {@code TaskView} if this view is for a bubble. + * Or get alpha for the overflow view if this view is for overflow. + * + * @return alpha for the content being shown */ - void setTaskViewAlpha(float alpha) { + public float getContentAlpha() { + if (mIsOverflow) { + return mOverflowView.getAlpha(); + } if (mTaskView != null) { + return mTaskView.getAlpha(); + } + return 1f; + } + + /** + * Set alpha of the underlying {@code TaskView} if this view is for a bubble. + * Or set alpha for the overflow view if this view is for overflow. + * + * Changing expanded view's alpha does not affect the {@code TaskView} since it uses a Surface. + */ + public void setContentAlpha(float alpha) { + if (mIsOverflow) { + mOverflowView.setAlpha(alpha); + } else if (mTaskView != null) { mTaskView.setAlpha(alpha); } - mPointerView.setAlpha(alpha); - setAlpha(alpha); + } + + /** + * Set translation Y for the expanded view content. + * Excludes manage button and pointer. + */ + public void setContentTranslationY(float translationY) { + mExpandedViewContainer.setTranslationY(translationY); + + // Left or right pointer can become detached when moving the view up + if (translationY <= 0 && (isShowingLeftPointer() || isShowingRightPointer())) { + // Y coordinate where the pointer would start to get detached from the expanded view. + // Takes into account bottom clipping and rounded corners + float detachPoint = + mExpandedViewContainer.getBottom() - mBottomClip - mCornerRadius + translationY; + float pointerBottom = mPointerPos.y + mPointerHeight; + // If pointer bottom is past detach point, move it in by that many pixels + float horizontalShift = 0; + if (pointerBottom > detachPoint) { + horizontalShift = pointerBottom - detachPoint; + } + if (isShowingLeftPointer()) { + // Move left pointer right + movePointerBy(horizontalShift, 0); + } else { + // Move right pointer left + movePointerBy(-horizontalShift, 0); + } + // Hide pointer if it is moved by entire width + mPointerView.setVisibility( + horizontalShift > mPointerWidth ? View.INVISIBLE : View.VISIBLE); + } + } + + /** + * Update alpha value for the manage button + */ + public void setManageButtonAlpha(float alpha) { + mManageButton.setAlpha(alpha); + } + + /** + * Set {@link #setTranslationY(float) translationY} for the manage button + */ + public void setManageButtonTranslationY(float translationY) { + mManageButton.setTranslationY(translationY); + } + + /** + * Set top clipping for the view + */ + public void setTopClip(int clip) { + mTopClip = clip; + onContainerClipUpdate(); + } + + /** + * Set bottom clipping for the view + */ + public void setBottomClip(int clip) { + mBottomClip = clip; + onContainerClipUpdate(); + } + + private void onContainerClipUpdate() { + if (mTopClip == 0 && mBottomClip == 0) { + if (mIsClipping) { + mIsClipping = false; + if (mTaskView != null) { + mTaskView.setClipBounds(null); + mTaskView.setEnableSurfaceClipping(false); + } + mExpandedViewContainer.invalidateOutline(); + } + } else { + if (!mIsClipping) { + mIsClipping = true; + if (mTaskView != null) { + mTaskView.setEnableSurfaceClipping(true); + } + } + mExpandedViewContainer.invalidateOutline(); + if (mTaskView != null) { + mTaskView.setClipBounds(new Rect(0, mTopClip, mTaskView.getWidth(), + mTaskView.getHeight() - mBottomClip)); + } + } + } + + /** + * Move pointer from base position + */ + public void movePointerBy(float x, float y) { + mPointerView.setTranslationX(mPointerPos.x + x); + mPointerView.setTranslationY(mPointerPos.y + y); } /** @@ -543,13 +705,13 @@ public class BubbleExpandedView extends LinearLayout { * Note that this contents visibility doesn't affect visibility at {@link android.view.View}, * and setting {@code false} actually means rendering the contents in transparent. */ - void setContentVisibility(boolean visibility) { + public void setContentVisibility(boolean visibility) { if (DEBUG_BUBBLE_EXPANDED_VIEW) { Log.d(TAG, "setContentVisibility: visibility=" + visibility + " bubble=" + getBubbleKey()); } mIsContentVisible = visibility; - if (mTaskView != null && !mIsAlphaAnimating) { + if (mTaskView != null && !mIsAnimating) { mTaskView.setAlpha(visibility ? 1f : 0f); mPointerView.setAlpha(visibility ? 1f : 0f); } @@ -560,6 +722,44 @@ public class BubbleExpandedView extends LinearLayout { return mTaskView; } + @VisibleForTesting + public BubbleOverflowContainerView getOverflow() { + return mOverflowView; + } + + + /** + * Return content height: taskView or overflow. + * Takes into account clippings set by {@link #setTopClip(int)} and {@link #setBottomClip(int)} + * + * @return if bubble is for overflow, return overflow height, otherwise return taskView height + */ + public int getContentHeight() { + if (mIsOverflow) { + return mOverflowView.getHeight() - mTopClip - mBottomClip; + } + if (mTaskView != null) { + return mTaskView.getHeight() - mTopClip - mBottomClip; + } + return 0; + } + + /** + * Return bottom position of the content on screen + * + * @return if bubble is for overflow, return value for overflow, otherwise taskView + */ + public int getContentBottomOnScreen() { + Rect out = new Rect(); + if (mIsOverflow) { + mOverflowView.getBoundsOnScreen(out); + } + if (mTaskView != null) { + mTaskView.getBoundsOnScreen(out); + } + return out.bottom; + } + int getTaskId() { return mTaskId; } @@ -687,7 +887,9 @@ public class BubbleExpandedView extends LinearLayout { mTaskView.onLocationChanged(); } if (mIsOverflow) { - mOverflowView.show(); + post(() -> { + mOverflowView.show(); + }); } } @@ -730,38 +932,59 @@ public class BubbleExpandedView extends LinearLayout { post(() -> { mCurrentPointer = showVertically ? onLeft ? mLeftPointer : mRightPointer : mTopPointer; updatePointerView(); - float pointerY; - float pointerX; if (showVertically) { - pointerY = bubbleCenter - (mPointerWidth / 2f); + mPointerPos.y = bubbleCenter - (mPointerWidth / 2f); if (!isRtl) { - pointerX = onLeft + mPointerPos.x = onLeft ? -mPointerHeight + mPointerOverlap : getWidth() - mPaddingRight - mPointerOverlap; } else { - pointerX = onLeft + mPointerPos.x = onLeft ? -(getWidth() - mPaddingLeft - mPointerOverlap) : mPointerHeight - mPointerOverlap; } } else { - pointerY = mPointerOverlap; + mPointerPos.y = mPointerOverlap; if (!isRtl) { - pointerX = bubbleCenter - (mPointerWidth / 2f); + mPointerPos.x = bubbleCenter - (mPointerWidth / 2f); } else { - pointerX = -(getWidth() - mPaddingLeft - bubbleCenter) + (mPointerWidth / 2f); + mPointerPos.x = -(getWidth() - mPaddingLeft - bubbleCenter) + + (mPointerWidth / 2f); } } if (animate) { - mPointerView.animate().translationX(pointerX).translationY(pointerY).start(); + mPointerView.animate().translationX(mPointerPos.x).translationY( + mPointerPos.y).start(); } else { - mPointerView.setTranslationY(pointerY); - mPointerView.setTranslationX(pointerX); + mPointerView.setTranslationY(mPointerPos.y); + mPointerView.setTranslationX(mPointerPos.x); mPointerView.setVisibility(VISIBLE); } }); } /** + * Return true if pointer is shown on the left + */ + public boolean isShowingLeftPointer() { + return mCurrentPointer == mLeftPointer; + } + + /** + * Return true if pointer is shown on the right + */ + public boolean isShowingRightPointer() { + return mCurrentPointer == mRightPointer; + } + + /** + * Return width of the current pointer + */ + public int getPointerWidth() { + return mPointerWidth; + } + + /** * Position of the manage button displayed in the expanded view. Used for placing user * education about the manage button. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java index fcd0ed7308ef..9aa285fff19c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflowContainerView.java @@ -167,7 +167,10 @@ public class BubbleOverflowContainerView extends LinearLayout { void updateOverflow() { Resources res = getResources(); - final int columns = res.getInteger(R.integer.bubbles_overflow_columns); + int columns = (int) Math.round(getWidth() + / (res.getDimension(R.dimen.bubble_name_width))); + columns = columns > 0 ? columns : res.getInteger(R.integer.bubbles_overflow_columns); + mRecyclerView.setLayoutManager( new OverflowGridLayoutManager(getContext(), columns)); if (mRecyclerView.getItemDecorationCount() == 0) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index e9729e45731b..dbad5df9cf56 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -16,6 +16,8 @@ package com.android.wm.shell.bubbles; +import static android.view.View.LAYOUT_DIRECTION_RTL; + import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; @@ -28,7 +30,6 @@ import android.graphics.Rect; import android.graphics.RectF; import android.util.Log; import android.view.Surface; -import android.view.View; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowMetrics; @@ -366,6 +367,14 @@ public class BubblePositioner { return mImeVisible ? mImeHeight : 0; } + /** Return top position of the IME if it's visible */ + public int getImeTop() { + if (mImeVisible) { + return getScreenRect().bottom - getImeHeight() - getInsets().bottom; + } + return 0; + } + /** Sets whether the IME is visible. **/ public void setImeVisible(boolean visible, int height) { mImeVisible = visible; @@ -557,16 +566,30 @@ public class BubblePositioner { * @return the position of the bubble on-screen when the stack is expanded. */ public PointF getExpandedBubbleXY(int index, BubbleStackView.StackViewState state) { - final float positionInRow = index * (mBubbleSize + mSpacingBetweenBubbles); + boolean showBubblesVertically = showBubblesVertically(); + boolean isRtl = mContext.getResources().getConfiguration().getLayoutDirection() + == LAYOUT_DIRECTION_RTL; + + int onScreenIndex; + if (showBubblesVertically || !isRtl) { + onScreenIndex = index; + } else { + // If bubbles are shown horizontally, check if RTL language is used. + // If RTL is active, position first bubble on the right and last on the left. + // Last bubble has screen index 0 and first bubble has max screen index value. + onScreenIndex = state.numberOfBubbles - 1 - index; + } + + final float positionInRow = onScreenIndex * (mBubbleSize + mSpacingBetweenBubbles); final float expandedStackSize = getExpandedStackSize(state.numberOfBubbles); - final float centerPosition = showBubblesVertically() + final float centerPosition = showBubblesVertically ? mPositionRect.centerY() : mPositionRect.centerX(); // alignment - centered on the edge final float rowStart = centerPosition - (expandedStackSize / 2f); float x; float y; - if (showBubblesVertically()) { + if (showBubblesVertically) { int inset = mExpandedViewLargeScreenInsetClosestEdge; y = rowStart + positionInRow; int left = mIsLargeScreen @@ -583,8 +606,8 @@ public class BubblePositioner { x = rowStart + positionInRow; } - if (showBubblesVertically() && mImeVisible) { - return new PointF(x, getExpandedBubbleYForIme(index, state)); + if (showBubblesVertically && mImeVisible) { + return new PointF(x, getExpandedBubbleYForIme(onScreenIndex, state)); } return new PointF(x, y); } @@ -693,7 +716,7 @@ public class BubblePositioner { // Start on the left if we're in LTR, right otherwise. final boolean startOnLeft = mContext.getResources().getConfiguration().getLayoutDirection() - != View.LAYOUT_DIRECTION_RTL; + != LAYOUT_DIRECTION_RTL; final float startingVerticalOffset = mContext.getResources().getDimensionPixelOffset( R.dimen.bubble_stack_starting_offset_y); // TODO: placement bug here because mPositionRect doesn't handle the overhanging edge @@ -749,4 +772,21 @@ public class BubblePositioner { public void setPinnedLocation(PointF point) { mPinLocation = point; } + + /** + * Navigation bar has an area where system gestures can be started from. + * + * @return {@link Rect} for system navigation bar gesture zone + */ + public Rect getNavBarGestureZone() { + // Gesture zone height from the bottom + int gestureZoneHeight = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.navigation_bar_gesture_height); + Rect screen = getScreenRect(); + return new Rect( + screen.left, + screen.bottom - gestureZoneHeight, + screen.right, + screen.bottom); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 0e8dc63943a6..33a5fa12a4a1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -21,6 +21,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.wm.shell.animation.Interpolators.ALPHA_IN; import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT; +import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE; import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; @@ -44,6 +45,7 @@ import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; +import android.os.SystemProperties; import android.provider.Settings; import android.util.Log; import android.view.Choreographer; @@ -56,6 +58,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; +import android.view.WindowManagerPolicyConstants; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.widget.FrameLayout; @@ -75,8 +78,12 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; import com.android.wm.shell.animation.PhysicsAnimator; +import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener; import com.android.wm.shell.bubbles.animation.AnimatableScaleMatrix; import com.android.wm.shell.bubbles.animation.ExpandedAnimationController; +import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationController; +import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationControllerImpl; +import com.android.wm.shell.bubbles.animation.ExpandedViewAnimationControllerStub; import com.android.wm.shell.bubbles.animation.PhysicsAnimationLayout; import com.android.wm.shell.bubbles.animation.StackAnimationController; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -97,6 +104,12 @@ import java.util.stream.Collectors; */ public class BubbleStackView extends FrameLayout implements ViewTreeObserver.OnComputeInternalInsetsListener { + /** + * Set to {@code true} to enable home gesture handling in bubbles + */ + public static final boolean HOME_GESTURE_ENABLED = + SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", false); + private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleStackView" : TAG_BUBBLES; /** How far the flyout needs to be dragged before it's dismissed regardless of velocity. */ @@ -148,7 +161,7 @@ public class BubbleStackView extends FrameLayout * Handler to use for all delayed animations - this way, we can easily cancel them before * starting a new animation. */ - private final ShellExecutor mDelayedAnimationExecutor; + private final ShellExecutor mMainExecutor; private Runnable mDelayedAnimation; /** @@ -197,6 +210,7 @@ public class BubbleStackView extends FrameLayout private PhysicsAnimationLayout mBubbleContainer; private StackAnimationController mStackAnimationController; private ExpandedAnimationController mExpandedAnimationController; + private ExpandedViewAnimationController mExpandedViewAnimationController; private View mScrim; private View mManageMenuScrim; @@ -276,6 +290,9 @@ public class BubbleStackView extends FrameLayout */ private int mPointerIndexDown = -1; + @Nullable + private BubblesNavBarGestureTracker mBubblesNavBarGestureTracker; + /** Description of current animation controller state. */ public void dump(PrintWriter pw, String[] args) { pw.println("Stack view state:"); @@ -693,6 +710,67 @@ public class BubbleStackView extends FrameLayout } }; + /** Touch listener set on the whole view that forwards event to the swipe up listener. */ + private final RelativeTouchListener mContainerSwipeListener = new RelativeTouchListener() { + @Override + public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) { + // Pass move event on to swipe listener + mSwipeUpListener.onDown(ev.getX(), ev.getY()); + return true; + } + + @Override + public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX, + float viewInitialY, float dx, float dy) { + // Pass move event on to swipe listener + mSwipeUpListener.onMove(dx, dy); + } + + @Override + public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX, + float viewInitialY, float dx, float dy, float velX, float velY) { + // Pass up even on to swipe listener + mSwipeUpListener.onUp(velX, velY); + } + }; + + /** MotionEventListener that listens from home gesture swipe event. */ + private final MotionEventListener mSwipeUpListener = new MotionEventListener() { + @Override + public void onDown(float x, float y) {} + + @Override + public void onMove(float dx, float dy) { + if ((mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) + || isStackEduShowing()) { + return; + } + + if (mShowingManage) { + showManageMenu(false /* show */); + } + // Only allow up + float collapsed = Math.min(dy, 0); + mExpandedViewAnimationController.updateDrag((int) -collapsed); + } + + @Override + public void onCancel() { + mExpandedViewAnimationController.animateBackToExpanded(); + } + + @Override + public void onUp(float velX, float velY) { + mExpandedViewAnimationController.setSwipeVelocity(velY); + if (mExpandedViewAnimationController.shouldCollapse()) { + // Update data first and start the animation when we are processing change + mBubbleData.setExpanded(false); + } else { + mExpandedViewAnimationController.animateBackToExpanded(); + } + } + }; + /** Click listener set on the flyout, which expands the stack when the flyout is tapped. */ private OnClickListener mFlyoutClickListener = new OnClickListener() { @Override @@ -766,7 +844,7 @@ public class BubbleStackView extends FrameLayout ShellExecutor mainExecutor) { super(context); - mDelayedAnimationExecutor = mainExecutor; + mMainExecutor = mainExecutor; mBubbleController = bubbleController; mBubbleData = data; @@ -796,6 +874,14 @@ public class BubbleStackView extends FrameLayout mExpandedAnimationController = new ExpandedAnimationController(mPositioner, onBubbleAnimatedOut, this); + + if (HOME_GESTURE_ENABLED) { + mExpandedViewAnimationController = + new ExpandedViewAnimationControllerImpl(context, mPositioner); + } else { + mExpandedViewAnimationController = new ExpandedViewAnimationControllerStub(); + } + mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or @@ -971,7 +1057,7 @@ public class BubbleStackView extends FrameLayout if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { // We need to be Z ordered on top in order for alpha animations to work. mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true); - mExpandedBubble.getExpandedView().setAlphaAnimating(true); + mExpandedBubble.getExpandedView().setAnimating(true); } } @@ -985,14 +1071,15 @@ public class BubbleStackView extends FrameLayout // = 0f remains in effect. && !mExpandedViewTemporarilyHidden) { mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false); - mExpandedBubble.getExpandedView().setAlphaAnimating(false); + mExpandedBubble.getExpandedView().setAnimating(false); } } }); mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> { if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { - mExpandedBubble.getExpandedView().setTaskViewAlpha( - (float) valueAnimator.getAnimatedValue()); + float alpha = (float) valueAnimator.getAnimatedValue(); + mExpandedBubble.getExpandedView().setContentAlpha(alpha); + mExpandedBubble.getExpandedView().setAlpha(alpha); } }); @@ -1795,6 +1882,7 @@ public class BubbleStackView extends FrameLayout private void showNewlySelectedBubble(BubbleViewProvider bubbleToSelect) { final BubbleViewProvider previouslySelected = mExpandedBubble; mExpandedBubble = bubbleToSelect; + mExpandedViewAnimationController.setExpandedView(mExpandedBubble.getExpandedView()); if (mIsExpanded) { hideCurrentInputMethod(); @@ -1843,12 +1931,19 @@ public class BubbleStackView extends FrameLayout return; } + boolean wasExpanded = mIsExpanded; + hideCurrentInputMethod(); mBubbleController.getSysuiProxy().onStackExpandChanged(shouldExpand); - if (mIsExpanded) { - animateCollapse(); + if (wasExpanded) { + stopMonitoringSwipeUpGesture(); + if (HOME_GESTURE_ENABLED) { + animateCollapse(); + } else { + animateCollapseWithScale(); + } logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED); } else { animateExpansion(); @@ -1856,11 +1951,58 @@ public class BubbleStackView extends FrameLayout logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED); logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED); + if (HOME_GESTURE_ENABLED) { + mBubbleController.isNotificationPanelExpanded(notifPanelExpanded -> { + if (!notifPanelExpanded && mIsExpanded) { + startMonitoringSwipeUpGesture(); + } + }); + } } notifyExpansionChanged(mExpandedBubble, mIsExpanded); } /** + * Monitor for swipe up gesture that is used to collapse expanded view + */ + void startMonitoringSwipeUpGesture() { + if (DEBUG_BUBBLE_GESTURE) { + Log.d(TAG, "startMonitoringSwipeUpGesture"); + } + stopMonitoringSwipeUpGestureInternal(); + + if (isGestureNavEnabled()) { + mBubblesNavBarGestureTracker = new BubblesNavBarGestureTracker(mContext, mPositioner); + mBubblesNavBarGestureTracker.start(mSwipeUpListener); + setOnTouchListener(mContainerSwipeListener); + } + } + + private boolean isGestureNavEnabled() { + return mContext.getResources().getInteger( + com.android.internal.R.integer.config_navBarInteractionMode) + == WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; + } + + /** + * Stop monitoring for swipe up gesture + */ + void stopMonitoringSwipeUpGesture() { + if (DEBUG_BUBBLE_GESTURE) { + Log.d(TAG, "stopMonitoringSwipeUpGesture"); + } + stopMonitoringSwipeUpGestureInternal(); + } + + private void stopMonitoringSwipeUpGestureInternal() { + if (mBubblesNavBarGestureTracker != null) { + mBubblesNavBarGestureTracker.stop(); + mBubblesNavBarGestureTracker = null; + setOnTouchListener(null); + } + } + + /** * Called when back press occurs while bubbles are expanded. */ public void onBackPressed() { @@ -2072,11 +2214,12 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); if (mExpandedBubble.getExpandedView() != null) { - mExpandedBubble.getExpandedView().setTaskViewAlpha(0f); + mExpandedBubble.getExpandedView().setContentAlpha(0f); + mExpandedBubble.getExpandedView().setAlpha(0f); // We'll be starting the alpha animation after a slight delay, so set this flag early // here. - mExpandedBubble.getExpandedView().setAlphaAnimating(true); + mExpandedBubble.getExpandedView().setAnimating(true); } mDelayedAnimation = () -> { @@ -2114,10 +2257,10 @@ public class BubbleStackView extends FrameLayout }) .start(); }; - mDelayedAnimationExecutor.executeDelayed(mDelayedAnimation, startDelay); + mMainExecutor.executeDelayed(mDelayedAnimation, startDelay); } - private void animateCollapse() { + private void animateCollapseWithScale() { cancelDelayedExpandCollapseSwitchAnimations(); if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) { @@ -2217,6 +2360,68 @@ public class BubbleStackView extends FrameLayout .start(); } + private void animateCollapse() { + cancelDelayedExpandCollapseSwitchAnimations(); + + if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) { + mManageEduView.hide(); + } + + mIsExpanded = false; + mIsExpansionAnimating = true; + + showScrim(false); + + mBubbleContainer.cancelAllAnimations(); + + // If we were in the middle of swapping, the animating-out surface would have been scaling + // to zero - finish it off. + PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel(); + mAnimatingOutSurfaceContainer.setScaleX(0f); + mAnimatingOutSurfaceContainer.setScaleY(0f); + + // Let the expanded animation controller know that it shouldn't animate child adds/reorders + // since we're about to animate collapsed. + mExpandedAnimationController.notifyPreparingToCollapse(); + + final Runnable collapseBackToStack = () -> mExpandedAnimationController.collapseBackToStack( + mStackAnimationController + .getStackPositionAlongNearestHorizontalEdge() + /* collapseTo */, + () -> mBubbleContainer.setActiveController(mStackAnimationController)); + + final Runnable after = () -> { + final BubbleViewProvider previouslySelected = mExpandedBubble; + // TODO(b/231350255): investigate why this call is needed here + beforeExpandedViewAnimation(); + if (mManageEduView != null) { + mManageEduView.hide(); + } + + if (DEBUG_BUBBLE_STACK_VIEW) { + Log.d(TAG, "animateCollapse"); + Log.d(TAG, BubbleDebugConfig.formatBubblesString(getBubblesOnScreen(), + mExpandedBubble)); + } + updateOverflowVisibility(); + updateZOrder(); + updateBadges(true /* setBadgeForCollapsedStack */); + afterExpandedViewAnimation(); + if (previouslySelected != null) { + previouslySelected.setTaskViewVisibility(false); + } + mExpandedViewAnimationController.reset(); + }; + mExpandedViewAnimationController.animateCollapse(collapseBackToStack, after); + if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { + // When the animation completes, we should no longer be showing the content. + // This won't actually update content visibility immediately, if we are currently + // animating. But updates the internal state for the content to be hidden after + // animation completes. + mExpandedBubble.getExpandedView().setContentVisibility(false); + } + } + private void animateSwitchBubbles() { // If we're no longer expanded, this is meaningless. if (!mIsExpanded) { @@ -2278,7 +2483,7 @@ public class BubbleStackView extends FrameLayout mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); - mDelayedAnimationExecutor.executeDelayed(() -> { + mMainExecutor.executeDelayed(() -> { if (!mIsExpanded) { mIsBubbleSwitchAnimating = false; return; @@ -2309,7 +2514,7 @@ public class BubbleStackView extends FrameLayout * animating flags for those animations. */ private void cancelDelayedExpandCollapseSwitchAnimations() { - mDelayedAnimationExecutor.removeCallbacks(mDelayedAnimation); + mMainExecutor.removeCallbacks(mDelayedAnimation); mIsExpansionAnimating = false; mIsBubbleSwitchAnimating = false; @@ -2333,9 +2538,18 @@ public class BubbleStackView extends FrameLayout /** * Updates the stack based for IME changes. When collapsed it'll move the stack if it * overlaps where they IME would be. When expanded it'll shift the expanded bubbles - * if they might overlap with the IME (this only happens for large screens). + * if they might overlap with the IME (this only happens for large screens) + * and clip the expanded view. */ - public void animateForIme(boolean visible) { + public void setImeVisible(boolean visible) { + if (HOME_GESTURE_ENABLED) { + setImeVisibleInternal(visible); + } else { + setImeVisibleWithoutClipping(visible); + } + } + + private void setImeVisibleWithoutClipping(boolean visible) { if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) { // This will update the animation so the bubbles move to position for the IME mExpandedAnimationController.expandFromStack(() -> { @@ -2386,6 +2600,62 @@ public class BubbleStackView extends FrameLayout } } + private void setImeVisibleInternal(boolean visible) { + if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) { + // This will update the animation so the bubbles move to position for the IME + mExpandedAnimationController.expandFromStack(() -> { + updatePointerPosition(false /* forIme */); + afterExpandedViewAnimation(); + mExpandedViewAnimationController.animateForImeVisibilityChange(visible); + } /* after */); + return; + } + + if (!mIsExpanded && getBubbleCount() > 0) { + final float stackDestinationY = + mStackAnimationController.animateForImeVisibility(visible); + + // How far the stack is animating due to IME, we'll just animate the flyout by that + // much too. + final float stackDy = + stackDestinationY - mStackAnimationController.getStackPosition().y; + + // If the flyout is visible, translate it along with the bubble stack. + if (mFlyout.getVisibility() == VISIBLE) { + PhysicsAnimator.getInstance(mFlyout) + .spring(DynamicAnimation.TRANSLATION_Y, + mFlyout.getTranslationY() + stackDy, + FLYOUT_IME_ANIMATION_SPRING_CONFIG) + .start(); + } + } + + if (mIsExpanded) { + mExpandedViewAnimationController.animateForImeVisibilityChange(visible); + if (mPositioner.showBubblesVertically() + && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { + float selectedY = mPositioner.getExpandedBubbleXY(getState().selectedIndex, + getState()).y; + float newExpandedViewTop = mPositioner.getExpandedViewY(mExpandedBubble, selectedY); + mExpandedBubble.getExpandedView().setImeVisible(visible); + if (!mExpandedBubble.getExpandedView().isUsingMaxHeight()) { + mExpandedViewContainer.animate().translationY(newExpandedViewTop); + } + List<Animator> animList = new ArrayList(); + for (int i = 0; i < mBubbleContainer.getChildCount(); i++) { + View child = mBubbleContainer.getChildAt(i); + float transY = mPositioner.getExpandedBubbleXY(i, getState()).y; + ObjectAnimator anim = ObjectAnimator.ofFloat(child, TRANSLATION_Y, transY); + animList.add(anim); + } + updatePointerPosition(true /* forIme */); + AnimatorSet set = new AnimatorSet(); + set.playTogether(animList); + set.start(); + } + } + } + @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() != MotionEvent.ACTION_DOWN && ev.getActionIndex() != mPointerIndexDown) { @@ -2821,7 +3091,7 @@ public class BubbleStackView extends FrameLayout && mExpandedBubble.getExpandedView() != null) { BubbleExpandedView bev = mExpandedBubble.getExpandedView(); bev.setContentVisibility(false); - bev.setAlphaAnimating(!mIsExpansionAnimating); + bev.setAnimating(!mIsExpansionAnimating); mExpandedViewContainerMatrix.setScaleX(0f); mExpandedViewContainerMatrix.setScaleY(0f); mExpandedViewContainerMatrix.setTranslate(0f, 0f); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 8a0db0a12711..0072da19b9ef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -214,6 +214,11 @@ public interface Bubbles { int modificationType); /** + * Called when notification panel is expanded or collapsed + */ + void onNotificationPanelExpandedChanged(boolean expanded); + + /** * Called when the status bar has become visible or invisible (either permanently or * temporarily). */ @@ -285,7 +290,7 @@ public interface Bubbles { /** Callback to tell SysUi components execute some methods. */ interface SysuiProxy { - void isNotificationShadeExpand(Consumer<Boolean> callback); + void isNotificationPanelExpand(Consumer<Boolean> callback); void getPendingOrActiveEntry(String key, Consumer<BubbleEntry> callback); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java new file mode 100644 index 000000000000..e7beeeb06534 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarGestureTracker.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2022 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.wm.shell.bubbles; + +import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; +import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; + +import android.content.Context; +import android.hardware.input.InputManager; +import android.util.Log; +import android.view.Choreographer; +import android.view.InputChannel; +import android.view.InputEventReceiver; +import android.view.InputMonitor; + +import androidx.annotation.Nullable; + +import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener; + +/** + * Set up tracking bubbles gestures that begin in navigation bar + */ +class BubblesNavBarGestureTracker { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "BubblesGestureTracker" : TAG_BUBBLES; + + private static final String GESTURE_MONITOR = "bubbles-gesture"; + private final Context mContext; + private final BubblePositioner mPositioner; + + @Nullable + private InputMonitor mInputMonitor; + @Nullable + private InputEventReceiver mInputEventReceiver; + + BubblesNavBarGestureTracker(Context context, BubblePositioner positioner) { + mContext = context; + mPositioner = positioner; + } + + /** + * Start tracking gestures + * + * @param listener listener that is notified of touch events + */ + void start(MotionEventListener listener) { + if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) { + Log.d(TAG, "start monitoring bubbles swipe up gesture"); + } + + stopInternal(); + + mInputMonitor = InputManager.getInstance().monitorGestureInput(GESTURE_MONITOR, + mContext.getDisplayId()); + InputChannel inputChannel = mInputMonitor.getInputChannel(); + + BubblesNavBarMotionEventHandler motionEventHandler = + new BubblesNavBarMotionEventHandler(mContext, mPositioner, + this::onInterceptTouch, listener); + mInputEventReceiver = new BubblesNavBarInputEventReceiver(inputChannel, + Choreographer.getInstance(), motionEventHandler); + } + + void stop() { + if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) { + Log.d(TAG, "stop monitoring bubbles swipe up gesture"); + } + stopInternal(); + } + + private void stopInternal() { + if (mInputEventReceiver != null) { + mInputEventReceiver.dispose(); + mInputEventReceiver = null; + } + if (mInputMonitor != null) { + mInputMonitor.dispose(); + mInputMonitor = null; + } + } + + private void onInterceptTouch() { + if (BubbleDebugConfig.DEBUG_BUBBLE_GESTURE) { + Log.d(TAG, "intercept touch event"); + } + if (mInputMonitor != null) { + mInputMonitor.pilferPointers(); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarInputEventReceiver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarInputEventReceiver.java new file mode 100644 index 000000000000..45037b87830f --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarInputEventReceiver.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 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.wm.shell.bubbles; + +import android.os.Looper; +import android.view.BatchedInputEventReceiver; +import android.view.Choreographer; +import android.view.InputChannel; +import android.view.InputEvent; +import android.view.MotionEvent; + +/** + * Bubbles {@link BatchedInputEventReceiver} for monitoring touches from navbar gesture area + */ +class BubblesNavBarInputEventReceiver extends BatchedInputEventReceiver { + + private final BubblesNavBarMotionEventHandler mMotionEventHandler; + + BubblesNavBarInputEventReceiver(InputChannel inputChannel, + Choreographer choreographer, BubblesNavBarMotionEventHandler motionEventHandler) { + super(inputChannel, Looper.myLooper(), choreographer); + mMotionEventHandler = motionEventHandler; + } + + @Override + public void onInputEvent(InputEvent event) { + boolean handled = false; + try { + if (!(event instanceof MotionEvent)) { + return; + } + handled = mMotionEventHandler.onMotionEvent((MotionEvent) event); + } finally { + finishInputEvent(event, handled); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java new file mode 100644 index 000000000000..844526ca0f35 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandler.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2022 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.wm.shell.bubbles; + +import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_GESTURE; +import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; +import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; + +import android.content.Context; +import android.graphics.PointF; +import android.util.Log; +import android.view.MotionEvent; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; + +import androidx.annotation.Nullable; + +/** + * Handles {@link MotionEvent}s for bubbles that begin in the nav bar area + */ +class BubblesNavBarMotionEventHandler { + private static final String TAG = + TAG_WITH_CLASS_NAME ? "BubblesNavBarMotionEventHandler" : TAG_BUBBLES; + private static final int VELOCITY_UNITS = 1000; + + private final Runnable mOnInterceptTouch; + private final MotionEventListener mMotionEventListener; + private final int mTouchSlop; + private final BubblePositioner mPositioner; + private final PointF mTouchDown = new PointF(); + private boolean mTrackingTouches; + private boolean mInterceptingTouches; + @Nullable + private VelocityTracker mVelocityTracker; + + BubblesNavBarMotionEventHandler(Context context, BubblePositioner positioner, + Runnable onInterceptTouch, MotionEventListener motionEventListener) { + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + mPositioner = positioner; + mOnInterceptTouch = onInterceptTouch; + mMotionEventListener = motionEventListener; + } + + /** + * Handle {@link MotionEvent} and forward it to {@code motionEventListener} defined in + * constructor + * + * @return {@code true} if this {@link MotionEvent} is handled (it started in the gesture area) + */ + public boolean onMotionEvent(MotionEvent motionEvent) { + float dx = motionEvent.getX() - mTouchDown.x; + float dy = motionEvent.getY() - mTouchDown.y; + + switch (motionEvent.getAction()) { + case MotionEvent.ACTION_DOWN: + if (isInGestureRegion(motionEvent)) { + mTouchDown.set(motionEvent.getX(), motionEvent.getY()); + mMotionEventListener.onDown(motionEvent.getX(), motionEvent.getY()); + mTrackingTouches = true; + return true; + } + break; + case MotionEvent.ACTION_MOVE: + if (mTrackingTouches) { + if (!mInterceptingTouches && Math.hypot(dx, dy) > mTouchSlop) { + mInterceptingTouches = true; + mOnInterceptTouch.run(); + } + if (mInterceptingTouches) { + getVelocityTracker().addMovement(motionEvent); + mMotionEventListener.onMove(dx, dy); + } + return true; + } + break; + case MotionEvent.ACTION_CANCEL: + if (mTrackingTouches) { + mMotionEventListener.onCancel(); + finishTracking(); + return true; + } + break; + case MotionEvent.ACTION_UP: + if (mTrackingTouches) { + if (mInterceptingTouches) { + getVelocityTracker().computeCurrentVelocity(VELOCITY_UNITS); + mMotionEventListener.onUp(getVelocityTracker().getXVelocity(), + getVelocityTracker().getYVelocity()); + } + finishTracking(); + return true; + } + break; + } + return false; + } + + private boolean isInGestureRegion(MotionEvent ev) { + // Only handles touch events beginning in navigation bar system gesture zone + if (mPositioner.getNavBarGestureZone().contains((int) ev.getX(), (int) ev.getY())) { + if (DEBUG_BUBBLE_GESTURE) { + Log.d(TAG, "handling touch y=" + ev.getY() + + " navBarGestureZone=" + mPositioner.getNavBarGestureZone()); + } + return true; + } + return false; + } + + private VelocityTracker getVelocityTracker() { + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + return mVelocityTracker; + } + + private void finishTracking() { + mTouchDown.set(0, 0); + mTrackingTouches = false; + mInterceptingTouches = false; + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + + /** + * Callback for receiving {@link MotionEvent} updates + */ + interface MotionEventListener { + /** + * Touch down action. + * + * @param x x coordinate + * @param y y coordinate + */ + void onDown(float x, float y); + + /** + * Move action. + * Reports distance from point reported in {@link #onDown(float, float)} + * + * @param dx distance moved on x-axis from starting point, in pixels + * @param dy distance moved on y-axis from starting point, in pixels + */ + void onMove(float dx, float dy); + + /** + * Touch up action. + * + * @param velX velocity of the move action on x axis + * @param velY velocity of the move actin on y axis + */ + void onUp(float velX, float velY); + + /** + * Motion action was cancelled. + */ + void onCancel(); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt index cf0cefec401a..ea9d065d5f53 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RelativeTouchListener.kt @@ -17,8 +17,6 @@ package com.android.wm.shell.bubbles import android.graphics.PointF -import android.os.Handler -import android.os.Looper import android.view.MotionEvent import android.view.VelocityTracker import android.view.View @@ -146,6 +144,12 @@ abstract class RelativeTouchListener : View.OnTouchListener { velocityTracker.clear() movedEnough = false } + + MotionEvent.ACTION_CANCEL -> { + v.handler.removeCallbacksAndMessages(null) + velocityTracker.clear() + movedEnough = false + } } return true diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java index 573f42468512..b521cb6a3d38 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedAnimationController.java @@ -16,12 +16,16 @@ package com.android.wm.shell.bubbles.animation; +import static android.view.View.LAYOUT_DIRECTION_RTL; + import static com.android.wm.shell.bubbles.BubblePositioner.NUM_VISIBLE_WHEN_RESTING; +import static com.android.wm.shell.bubbles.BubbleStackView.HOME_GESTURE_ENABLED; import android.content.res.Resources; import android.graphics.Path; import android.graphics.PointF; import android.view.View; +import android.view.animation.Interpolator; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -61,7 +65,10 @@ public class ExpandedAnimationController private static final float DAMPING_RATIO_MEDIUM_LOW_BOUNCY = 0.65f; /** Stiffness for the expand/collapse path-following animation. */ - private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 1000; + private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS = 400; + + /** Stiffness for the expand/collapse animation when home gesture handling is off */ + private static final int EXPAND_COLLAPSE_ANIM_STIFFNESS_WITHOUT_HOME_GESTURE = 1000; /** * Velocity required to dismiss an individual bubble without dragging it into the dismiss @@ -73,6 +80,11 @@ public class ExpandedAnimationController new PhysicsAnimator.SpringConfig( EXPAND_COLLAPSE_ANIM_STIFFNESS, SpringForce.DAMPING_RATIO_NO_BOUNCY); + private final PhysicsAnimator.SpringConfig mAnimateOutSpringConfigWithoutHomeGesture = + new PhysicsAnimator.SpringConfig( + EXPAND_COLLAPSE_ANIM_STIFFNESS_WITHOUT_HOME_GESTURE, + SpringForce.DAMPING_RATIO_NO_BOUNCY); + /** Horizontal offset between bubbles, which we need to know to re-stack them. */ private float mStackOffsetPx; /** Size of each bubble. */ @@ -233,6 +245,11 @@ public class ExpandedAnimationController }; } + boolean showBubblesVertically = mPositioner.showBubblesVertically(); + final boolean isRtl = + mLayout.getContext().getResources().getConfiguration().getLayoutDirection() + == LAYOUT_DIRECTION_RTL; + // Animate each bubble individually, since each path will end in a different spot. animationsForChildrenFromIndex(0, (index, animation) -> { final View bubble = mLayout.getChildAt(index); @@ -267,9 +284,20 @@ public class ExpandedAnimationController // right side, the first bubble is traveling to the top left, so it leads. During // collapse to the left, the first bubble has the shortest travel time back to the stack // position, so it leads (and vice versa). - final boolean firstBubbleLeads = - (expanding && !mLayout.isFirstChildXLeftOfCenter(bubble.getTranslationX())) + final boolean firstBubbleLeads; + if (showBubblesVertically || !isRtl) { + firstBubbleLeads = + (expanding && !mLayout.isFirstChildXLeftOfCenter(bubble.getTranslationX())) || (!expanding && mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x)); + } else { + // For RTL languages, when showing bubbles horizontally, it is reversed. The bubbles + // are positioned right to left. This means that when expanding from left, the top + // bubble will lead as it will be positioned on the right. And when expanding from + // right, the top bubble will have the least travel distance. + firstBubbleLeads = + (expanding && mLayout.isFirstChildXLeftOfCenter(bubble.getTranslationX())) + || (!expanding && !mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x)); + } final int startDelay = firstBubbleLeads ? (index * 10) : ((mLayout.getChildCount() - index) * 10); @@ -278,11 +306,20 @@ public class ExpandedAnimationController (firstBubbleLeads && index == 0) || (!firstBubbleLeads && index == mLayout.getChildCount() - 1); + Interpolator interpolator; + if (HOME_GESTURE_ENABLED) { + // When home gesture is enabled, we use a different animation timing for collapse + interpolator = expanding + ? Interpolators.EMPHASIZED_ACCELERATE : Interpolators.EMPHASIZED_DECELERATE; + } else { + interpolator = Interpolators.LINEAR; + } + animation .followAnimatedTargetAlongPath( path, EXPAND_COLLAPSE_TARGET_ANIM_DURATION /* targetAnimDuration */, - Interpolators.LINEAR /* targetAnimInterpolator */, + interpolator /* targetAnimInterpolator */, isLeadBubble ? mLeadBubbleEndAction : null /* endAction */, () -> mLeadBubbleEndAction = null /* endAction */) .withStartDelay(startDelay) @@ -525,10 +562,16 @@ public class ExpandedAnimationController finishRemoval.run(); mOnBubbleAnimatedOutAction.run(); } else { + PhysicsAnimator.SpringConfig springConfig; + if (HOME_GESTURE_ENABLED) { + springConfig = mAnimateOutSpringConfig; + } else { + springConfig = mAnimateOutSpringConfigWithoutHomeGesture; + } PhysicsAnimator.getInstance(child) .spring(DynamicAnimation.ALPHA, 0f) - .spring(DynamicAnimation.SCALE_X, 0f, mAnimateOutSpringConfig) - .spring(DynamicAnimation.SCALE_Y, 0f, mAnimateOutSpringConfig) + .spring(DynamicAnimation.SCALE_X, 0f, springConfig) + .spring(DynamicAnimation.SCALE_Y, 0f, springConfig) .withEndActions(finishRemoval, mOnBubbleAnimatedOutAction) .start(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationController.java new file mode 100644 index 000000000000..8a33780bc8d5 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationController.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2022 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.wm.shell.bubbles.animation; + +import com.android.wm.shell.bubbles.BubbleExpandedView; + +/** + * Animation controller for bubble expanded view collapsing + */ +public interface ExpandedViewAnimationController { + /** + * Set expanded view that this controller is working with. + */ + void setExpandedView(BubbleExpandedView expandedView); + + /** + * Set current collapse value, in pixels. + * + * @param distance pixels that user dragged the view by + */ + void updateDrag(float distance); + + /** + * Set current swipe velocity. + * Velocity is directional: + * <ul> + * <li>velocity < 0 means swipe direction is up</li> + * <li>velocity > 0 means swipe direction is down</li> + * </ul> + */ + void setSwipeVelocity(float velocity); + + /** + * Check if view is dragged past collapse threshold or swipe up velocity exceeds min velocity + * required to collapse the view + */ + boolean shouldCollapse(); + + /** + * Animate view to collapsed state + * + * @param startStackCollapse runnable that is triggered when bubbles can start moving back to + * their collapsed location + * @param after runnable to run after animation is complete + */ + void animateCollapse(Runnable startStackCollapse, Runnable after); + + /** + * Animate the view back to fully expanded state. + */ + void animateBackToExpanded(); + + /** + * Animate view for IME visibility change + */ + void animateForImeVisibilityChange(boolean visible); + + /** + * Reset the view to fully expanded state + */ + void reset(); +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java new file mode 100644 index 000000000000..ca54232c95b5 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerImpl.java @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2022 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.wm.shell.bubbles.animation; + +import static android.view.View.ALPHA; + +import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_COLLAPSE_ANIMATOR; +import static com.android.wm.shell.bubbles.BubbleDebugConfig.DEBUG_EXPANDED_VIEW_DRAGGING; +import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; +import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.wm.shell.bubbles.BubbleExpandedView.BOTTOM_CLIP_PROPERTY; +import static com.android.wm.shell.bubbles.BubbleExpandedView.CONTENT_ALPHA; +import static com.android.wm.shell.bubbles.BubbleExpandedView.MANAGE_BUTTON_ALPHA; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.annotation.SuppressLint; +import android.content.Context; +import android.util.Log; +import android.view.HapticFeedbackConstants; +import android.view.ViewConfiguration; + +import androidx.annotation.Nullable; +import androidx.dynamicanimation.animation.DynamicAnimation; +import androidx.dynamicanimation.animation.FloatPropertyCompat; +import androidx.dynamicanimation.animation.SpringAnimation; +import androidx.dynamicanimation.animation.SpringForce; + +import com.android.wm.shell.animation.FlingAnimationUtils; +import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.bubbles.BubbleExpandedView; +import com.android.wm.shell.bubbles.BubblePositioner; + +import java.util.ArrayList; +import java.util.List; + +/** + * Implementation of {@link ExpandedViewAnimationController} that uses a collapse animation to + * hide the {@link BubbleExpandedView} + */ +public class ExpandedViewAnimationControllerImpl implements ExpandedViewAnimationController { + + private static final String TAG = TAG_WITH_CLASS_NAME ? "ExpandedViewAnimCtrl" : TAG_BUBBLES; + + private static final float COLLAPSE_THRESHOLD = 0.02f; + + private static final int COLLAPSE_DURATION_MS = 250; + + private static final int MANAGE_BUTTON_ANIM_DURATION_MS = 78; + + private static final int CONTENT_OPACITY_ANIM_DELAY_MS = 93; + private static final int CONTENT_OPACITY_ANIM_DURATION_MS = 78; + + private static final int BACKGROUND_OPACITY_ANIM_DELAY_MS = 172; + private static final int BACKGROUND_OPACITY_ANIM_DURATION_MS = 78; + + /** Animation fraction threshold for content alpha animation when stack collapse should begin */ + private static final float STACK_COLLAPSE_THRESHOLD = 0.5f; + + private static final FloatPropertyCompat<ExpandedViewAnimationControllerImpl> + COLLAPSE_HEIGHT_PROPERTY = + new FloatPropertyCompat<ExpandedViewAnimationControllerImpl>("CollapseSpring") { + @Override + public float getValue(ExpandedViewAnimationControllerImpl controller) { + return controller.getCollapsedAmount(); + } + + @Override + public void setValue(ExpandedViewAnimationControllerImpl controller, + float value) { + controller.setCollapsedAmount(value); + } + }; + + private final int mMinFlingVelocity; + private float mSwipeUpVelocity; + private float mSwipeDownVelocity; + private final BubblePositioner mPositioner; + private final FlingAnimationUtils mFlingAnimationUtils; + private int mDraggedAmount; + private float mCollapsedAmount; + @Nullable + private BubbleExpandedView mExpandedView; + @Nullable + private AnimatorSet mCollapseAnimation; + private boolean mNotifiedAboutThreshold; + private SpringAnimation mBackToExpandedAnimation; + @Nullable + private ObjectAnimator mBottomClipAnim; + + public ExpandedViewAnimationControllerImpl(Context context, BubblePositioner positioner) { + mFlingAnimationUtils = new FlingAnimationUtils(context.getResources().getDisplayMetrics(), + COLLAPSE_DURATION_MS / 1000f); + mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity(); + mPositioner = positioner; + } + + private static void adjustAnimatorSetDuration(AnimatorSet animatorSet, + float durationAdjustment) { + for (Animator animator : animatorSet.getChildAnimations()) { + animator.setStartDelay((long) (animator.getStartDelay() * durationAdjustment)); + animator.setDuration((long) (animator.getDuration() * durationAdjustment)); + } + } + + @Override + public void setExpandedView(BubbleExpandedView expandedView) { + if (mExpandedView != null) { + if (DEBUG_COLLAPSE_ANIMATOR) { + Log.d(TAG, "updating expandedView, resetting previous"); + } + if (mCollapseAnimation != null) { + mCollapseAnimation.cancel(); + } + if (mBackToExpandedAnimation != null) { + mBackToExpandedAnimation.cancel(); + } + reset(); + } + mExpandedView = expandedView; + } + + @Override + public void updateDrag(float distance) { + if (mExpandedView != null) { + mDraggedAmount = OverScroll.dampedScroll(distance, mExpandedView.getContentHeight()); + + if (DEBUG_COLLAPSE_ANIMATOR && DEBUG_EXPANDED_VIEW_DRAGGING) { + Log.d(TAG, "updateDrag: distance=" + distance + " dragged=" + mDraggedAmount); + } + + setCollapsedAmount(mDraggedAmount); + + if (!mNotifiedAboutThreshold && isPastCollapseThreshold()) { + mNotifiedAboutThreshold = true; + if (DEBUG_COLLAPSE_ANIMATOR) { + Log.d(TAG, "notifying over collapse threshold"); + } + vibrateIfEnabled(); + } + } + } + + @Override + public void setSwipeVelocity(float velocity) { + if (velocity < 0) { + mSwipeUpVelocity = Math.abs(velocity); + mSwipeDownVelocity = 0; + } else { + mSwipeUpVelocity = 0; + mSwipeDownVelocity = velocity; + } + } + + @Override + public boolean shouldCollapse() { + if (mSwipeDownVelocity > mMinFlingVelocity) { + // Swipe velocity is positive and over fling velocity. + // This is a swipe down, always reset to expanded state, regardless of dragged amount. + if (DEBUG_COLLAPSE_ANIMATOR) { + Log.d(TAG, + "not collapsing expanded view, swipe down velocity: " + mSwipeDownVelocity + + " minV: " + mMinFlingVelocity); + } + return false; + } + + if (mSwipeUpVelocity > mMinFlingVelocity) { + // Swiping up and over fling velocity, collapse the view. + if (DEBUG_COLLAPSE_ANIMATOR) { + Log.d(TAG, + "collapse expanded view, swipe up velocity: " + mSwipeUpVelocity + " minV: " + + mMinFlingVelocity); + } + return true; + } + + if (isPastCollapseThreshold()) { + if (DEBUG_COLLAPSE_ANIMATOR) { + Log.d(TAG, "collapse expanded view, past threshold, dragged: " + mDraggedAmount); + } + return true; + } + + if (DEBUG_COLLAPSE_ANIMATOR) { + Log.d(TAG, "not collapsing expanded view"); + } + + return false; + } + + @Override + public void animateCollapse(Runnable startStackCollapse, Runnable after) { + if (DEBUG_COLLAPSE_ANIMATOR) { + Log.d(TAG, + "expandedView animate collapse swipeVel=" + mSwipeUpVelocity + " minFlingVel=" + + mMinFlingVelocity); + } + if (mExpandedView != null) { + // Mark it as animating immediately to avoid updates to the view before animation starts + mExpandedView.setAnimating(true); + + if (mCollapseAnimation != null) { + mCollapseAnimation.cancel(); + } + mCollapseAnimation = createCollapseAnimation(mExpandedView, startStackCollapse, after); + + if (mSwipeUpVelocity >= mMinFlingVelocity) { + int contentHeight = mExpandedView.getContentHeight(); + + // Use a temp animator to get adjusted duration value for swipe. + // This new value will be used to adjust animation times proportionally in the + // animator set. If we adjust animator set duration directly, all child animations + // will get the same animation time. + ValueAnimator tempAnimator = new ValueAnimator(); + mFlingAnimationUtils.applyDismissing(tempAnimator, mCollapsedAmount, contentHeight, + mSwipeUpVelocity, (contentHeight - mCollapsedAmount)); + + float durationAdjustment = + (float) tempAnimator.getDuration() / COLLAPSE_DURATION_MS; + + adjustAnimatorSetDuration(mCollapseAnimation, durationAdjustment); + mCollapseAnimation.setInterpolator(tempAnimator.getInterpolator()); + } + mCollapseAnimation.start(); + } + } + + @Override + public void animateBackToExpanded() { + if (DEBUG_COLLAPSE_ANIMATOR) { + Log.d(TAG, "expandedView animate back to expanded"); + } + BubbleExpandedView expandedView = mExpandedView; + if (expandedView == null) { + return; + } + + expandedView.setAnimating(true); + + mBackToExpandedAnimation = new SpringAnimation(this, COLLAPSE_HEIGHT_PROPERTY); + mBackToExpandedAnimation.setSpring(new SpringForce() + .setStiffness(SpringForce.STIFFNESS_LOW) + .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY) + ); + mBackToExpandedAnimation.addEndListener(new OneTimeEndListener() { + @Override + public void onAnimationEnd(DynamicAnimation animation, boolean canceled, float value, + float velocity) { + super.onAnimationEnd(animation, canceled, value, velocity); + mNotifiedAboutThreshold = false; + mBackToExpandedAnimation = null; + expandedView.setAnimating(false); + } + }); + mBackToExpandedAnimation.setStartValue(mCollapsedAmount); + mBackToExpandedAnimation.animateToFinalPosition(0); + } + + @Override + public void animateForImeVisibilityChange(boolean visible) { + if (mExpandedView != null) { + if (mBottomClipAnim != null) { + mBottomClipAnim.cancel(); + } + int clip = 0; + if (visible) { + // Clip the expanded view at the top of the IME view + clip = mExpandedView.getContentBottomOnScreen() - mPositioner.getImeTop(); + // Don't allow negative clip value + clip = Math.max(clip, 0); + } + mBottomClipAnim = ObjectAnimator.ofInt(mExpandedView, BOTTOM_CLIP_PROPERTY, clip); + mBottomClipAnim.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBottomClipAnim = null; + } + }); + mBottomClipAnim.start(); + } + } + + @Override + public void reset() { + if (DEBUG_COLLAPSE_ANIMATOR) { + Log.d(TAG, "reset expandedView collapsed state"); + } + if (mExpandedView == null) { + return; + } + mExpandedView.setAnimating(false); + + if (mCollapseAnimation != null) { + mCollapseAnimation.cancel(); + } + if (mBackToExpandedAnimation != null) { + mBackToExpandedAnimation.cancel(); + } + mExpandedView.setContentAlpha(1); + mExpandedView.setAlpha(1); + mExpandedView.setManageButtonAlpha(1); + setCollapsedAmount(0); + mExpandedView.setBottomClip(0); + mExpandedView.movePointerBy(0, 0); + mCollapsedAmount = 0; + mDraggedAmount = 0; + mSwipeUpVelocity = 0; + mSwipeDownVelocity = 0; + mNotifiedAboutThreshold = false; + } + + private float getCollapsedAmount() { + return mCollapsedAmount; + } + + private void setCollapsedAmount(float collapsed) { + if (mCollapsedAmount != collapsed) { + float previous = mCollapsedAmount; + mCollapsedAmount = collapsed; + + if (mExpandedView != null) { + if (previous == 0) { + // View was not collapsed before. Apply z order change + mExpandedView.setSurfaceZOrderedOnTop(true); + mExpandedView.setAnimating(true); + } + + mExpandedView.setTopClip((int) mCollapsedAmount); + // Move up with translationY. Use negative collapsed value + mExpandedView.setContentTranslationY(-mCollapsedAmount); + mExpandedView.setManageButtonTranslationY(-mCollapsedAmount); + + if (mCollapsedAmount == 0) { + // View is no longer collapsed. Revert z order change + mExpandedView.setSurfaceZOrderedOnTop(false); + mExpandedView.setAnimating(false); + } + } + } + } + + private boolean isPastCollapseThreshold() { + if (mExpandedView != null) { + return mDraggedAmount > mExpandedView.getContentHeight() * COLLAPSE_THRESHOLD; + } + return false; + } + + private AnimatorSet createCollapseAnimation(BubbleExpandedView expandedView, + Runnable startStackCollapse, Runnable after) { + List<Animator> animatorList = new ArrayList<>(); + animatorList.add(createHeightAnimation(expandedView)); + animatorList.add(createManageButtonAnimation()); + ObjectAnimator contentAlphaAnimation = createContentAlphaAnimation(); + final boolean[] notified = {false}; + contentAlphaAnimation.addUpdateListener(animation -> { + if (!notified[0] && animation.getAnimatedFraction() > STACK_COLLAPSE_THRESHOLD) { + notified[0] = true; + // Notify bubbles that they can start moving back to the collapsed position + startStackCollapse.run(); + } + }); + animatorList.add(contentAlphaAnimation); + animatorList.add(createBackgroundAlphaAnimation()); + + AnimatorSet animatorSet = new AnimatorSet(); + animatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + after.run(); + } + }); + animatorSet.playTogether(animatorList); + return animatorSet; + } + + private ValueAnimator createHeightAnimation(BubbleExpandedView expandedView) { + ValueAnimator animator = ValueAnimator.ofInt((int) mCollapsedAmount, + expandedView.getContentHeight()); + animator.setInterpolator(Interpolators.EMPHASIZED_ACCELERATE); + animator.setDuration(COLLAPSE_DURATION_MS); + animator.addUpdateListener(anim -> setCollapsedAmount((int) anim.getAnimatedValue())); + return animator; + } + + private ObjectAnimator createManageButtonAnimation() { + ObjectAnimator animator = ObjectAnimator.ofFloat(mExpandedView, MANAGE_BUTTON_ALPHA, 0f); + animator.setDuration(MANAGE_BUTTON_ANIM_DURATION_MS); + animator.setInterpolator(Interpolators.LINEAR); + return animator; + } + + private ObjectAnimator createContentAlphaAnimation() { + ObjectAnimator animator = ObjectAnimator.ofFloat(mExpandedView, CONTENT_ALPHA, 0f); + animator.setDuration(CONTENT_OPACITY_ANIM_DURATION_MS); + animator.setInterpolator(Interpolators.LINEAR); + animator.setStartDelay(CONTENT_OPACITY_ANIM_DELAY_MS); + return animator; + } + + private ObjectAnimator createBackgroundAlphaAnimation() { + ObjectAnimator animator = ObjectAnimator.ofFloat(mExpandedView, ALPHA, 0f); + animator.setDuration(BACKGROUND_OPACITY_ANIM_DURATION_MS); + animator.setInterpolator(Interpolators.LINEAR); + animator.setStartDelay(BACKGROUND_OPACITY_ANIM_DELAY_MS); + return animator; + } + + @SuppressLint("MissingPermission") + private void vibrateIfEnabled() { + if (mExpandedView != null) { + mExpandedView.performHapticFeedback(HapticFeedbackConstants.DRAG_CROSSING); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java new file mode 100644 index 000000000000..bb8a3aaaf551 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerStub.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 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.wm.shell.bubbles.animation; + +import com.android.wm.shell.bubbles.BubbleExpandedView; + +/** + * Stub implementation {@link ExpandedViewAnimationController} that does not animate the + * {@link BubbleExpandedView} + */ +public class ExpandedViewAnimationControllerStub implements ExpandedViewAnimationController { + @Override + public void setExpandedView(BubbleExpandedView expandedView) { + } + + @Override + public void updateDrag(float distance) { + } + + @Override + public void setSwipeVelocity(float velocity) { + } + + @Override + public boolean shouldCollapse() { + return false; + } + + @Override + public void animateCollapse(Runnable startStackCollapse, Runnable after) { + } + + @Override + public void animateBackToExpanded() { + } + + @Override + public void animateForImeVisibilityChange(boolean visible) { + } + + @Override + public void reset() { + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/OverScroll.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/OverScroll.java new file mode 100644 index 000000000000..d4e76ed0282e --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/animation/OverScroll.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 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.wm.shell.bubbles.animation; + +/** + * Utility methods for overscroll damping and related effect. + * + * Copied from packages/apps/Launcher3/src/com/android/launcher3/touch/OverScroll.java + */ +public class OverScroll { + + private static final float OVERSCROLL_DAMP_FACTOR = 0.07f; + + /** + * This curve determines how the effect of scrolling over the limits of the page diminishes + * as the user pulls further and further from the bounds + * + * @param f The percentage of how much the user has overscrolled. + * @return A transformed percentage based on the influence curve. + */ + private static float overScrollInfluenceCurve(float f) { + f -= 1.0f; + return f * f * f + 1.0f; + } + + /** + * @param amount The original amount overscrolled. + * @param max The maximum amount that the View can overscroll. + * @return The dampened overscroll amount. + */ + public static int dampedScroll(float amount, int max) { + if (Float.compare(amount, 0) == 0) return 0; + + float f = amount / max; + f = f / (Math.abs(f)) * (overScrollInfluenceCurve(Math.abs(f))); + + // Clamp this factor, f, to -1 < f < 1 + if (Math.abs(f) >= 1) { + f /= Math.abs(f); + } + + return Math.round(OVERSCROLL_DAMP_FACTOR * f * max); + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java index c32733d4f73c..28c7367662a2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayChangeController.java @@ -16,11 +16,13 @@ package com.android.wm.shell.common; +import android.annotation.Nullable; import android.os.RemoteException; import android.util.Slog; -import android.view.IDisplayWindowRotationCallback; -import android.view.IDisplayWindowRotationController; +import android.view.IDisplayChangeWindowCallback; +import android.view.IDisplayChangeWindowController; import android.view.IWindowManager; +import android.window.DisplayAreaInfo; import android.window.WindowContainerTransaction; import androidx.annotation.BinderThread; @@ -40,17 +42,17 @@ public class DisplayChangeController { private final ShellExecutor mMainExecutor; private final IWindowManager mWmService; - private final IDisplayWindowRotationController mControllerImpl; + private final IDisplayChangeWindowController mControllerImpl; - private final CopyOnWriteArrayList<OnDisplayChangingListener> mRotationListener = + private final CopyOnWriteArrayList<OnDisplayChangingListener> mDisplayChangeListener = new CopyOnWriteArrayList<>(); public DisplayChangeController(IWindowManager wmService, ShellExecutor mainExecutor) { mMainExecutor = mainExecutor; mWmService = wmService; - mControllerImpl = new DisplayWindowRotationControllerImpl(); + mControllerImpl = new DisplayChangeWindowControllerImpl(); try { - mWmService.setDisplayWindowRotationController(mControllerImpl); + mWmService.setDisplayChangeWindowController(mControllerImpl); } catch (RemoteException e) { throw new RuntimeException("Unable to register rotation controller"); } @@ -59,63 +61,64 @@ public class DisplayChangeController { /** * Adds a display rotation controller. */ - public void addRotationListener(OnDisplayChangingListener listener) { - mRotationListener.add(listener); + public void addDisplayChangeListener(OnDisplayChangingListener listener) { + mDisplayChangeListener.add(listener); } /** * Removes a display rotation controller. */ - public void removeRotationListener(OnDisplayChangingListener listener) { - mRotationListener.remove(listener); + public void removeDisplayChangeListener(OnDisplayChangingListener listener) { + mDisplayChangeListener.remove(listener); } - /** Query all listeners for changes that should happen on rotation. */ - public void dispatchOnRotateDisplay(WindowContainerTransaction outWct, int displayId, - final int fromRotation, final int toRotation) { - for (OnDisplayChangingListener c : mRotationListener) { - c.onRotateDisplay(displayId, fromRotation, toRotation, outWct); + /** Query all listeners for changes that should happen on display change. */ + public void dispatchOnDisplayChange(WindowContainerTransaction outWct, int displayId, + int fromRotation, int toRotation, DisplayAreaInfo newDisplayAreaInfo) { + for (OnDisplayChangingListener c : mDisplayChangeListener) { + c.onDisplayChange(displayId, fromRotation, toRotation, newDisplayAreaInfo, outWct); } } - private void onRotateDisplay(int displayId, final int fromRotation, final int toRotation, - IDisplayWindowRotationCallback callback) { + private void onDisplayChange(int displayId, int fromRotation, int toRotation, + DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback) { WindowContainerTransaction t = new WindowContainerTransaction(); - dispatchOnRotateDisplay(t, displayId, fromRotation, toRotation); + dispatchOnDisplayChange(t, displayId, fromRotation, toRotation, newDisplayAreaInfo); try { - callback.continueRotateDisplay(toRotation, t); + callback.continueDisplayChange(t); } catch (RemoteException e) { - Slog.e(TAG, "Failed to continue rotation", e); + Slog.e(TAG, "Failed to continue handling display change", e); } } @BinderThread - private class DisplayWindowRotationControllerImpl - extends IDisplayWindowRotationController.Stub { + private class DisplayChangeWindowControllerImpl + extends IDisplayChangeWindowController.Stub { @Override - public void onRotateDisplay(int displayId, final int fromRotation, - final int toRotation, IDisplayWindowRotationCallback callback) { - mMainExecutor.execute(() -> { - DisplayChangeController.this.onRotateDisplay(displayId, fromRotation, toRotation, - callback); - }); + public void onDisplayChange(int displayId, int fromRotation, int toRotation, + DisplayAreaInfo newDisplayAreaInfo, IDisplayChangeWindowCallback callback) { + mMainExecutor.execute(() -> DisplayChangeController.this + .onDisplayChange(displayId, fromRotation, toRotation, + newDisplayAreaInfo, callback)); } } /** * Give a listener a chance to queue up configuration changes to execute as part of a - * display rotation. The contents of {@link #onRotateDisplay} must run synchronously. + * display rotation. The contents of {@link #onDisplayChange} must run synchronously. */ @ShellMainThread public interface OnDisplayChangingListener { /** - * Called before the display is rotated. Contents of this method must run synchronously. - * @param displayId Id of display that is rotating. - * @param fromRotation starting rotation of the display. - * @param toRotation target rotation of the display (after rotating). + * Called before the display size has changed. + * Contents of this method must run synchronously. + * @param displayId display id of the display that is under the change + * @param fromRotation rotation before the change + * @param toRotation rotation after the change + * @param newDisplayAreaInfo display area info after applying the update * @param t A task transaction to populate. */ - void onRotateDisplay(int displayId, int fromRotation, int toRotation, - WindowContainerTransaction t); + void onDisplayChange(int displayId, int fromRotation, int toRotation, + @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction t); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java index 4ba32e93fb3d..764936cceb01 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayController.java @@ -156,14 +156,14 @@ public class DisplayController { * Adds a display rotation controller. */ public void addDisplayChangingController(OnDisplayChangingListener controller) { - mChangeController.addRotationListener(controller); + mChangeController.addDisplayChangeListener(controller); } /** * Removes a display rotation controller. */ public void removeDisplayChangingController(OnDisplayChangingListener controller) { - mChangeController.removeRotationListener(controller); + mChangeController.removeDisplayChangeListener(controller); } private void onDisplayAdded(int displayId) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index db6131a17114..1d10bbe37438 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -38,8 +38,6 @@ import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.TaskViewFactoryController; import com.android.wm.shell.TaskViewTransitions; import com.android.wm.shell.WindowManagerShellWrapper; -import com.android.wm.shell.apppairs.AppPairs; -import com.android.wm.shell.apppairs.AppPairsController; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.back.BackAnimationController; import com.android.wm.shell.bubbles.BubbleController; @@ -70,8 +68,6 @@ import com.android.wm.shell.fullscreen.FullscreenUnfoldController; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController; import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; @@ -319,26 +315,27 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController( - @DynamicOverride Optional<FullscreenUnfoldController> fullscreenUnfoldController, + @DynamicOverride Lazy<Optional<FullscreenUnfoldController>> fullscreenUnfoldController, Optional<ShellUnfoldProgressProvider> progressProvider) { if (progressProvider.isPresent() && progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) { - return fullscreenUnfoldController; + return fullscreenUnfoldController.get(); } return Optional.empty(); } + @BindsOptionalOf + @DynamicOverride + abstract UnfoldTransitionHandler optionalUnfoldTransitionHandler(); + @WMSingleton @Provides static Optional<UnfoldTransitionHandler> provideUnfoldTransitionHandler( Optional<ShellUnfoldProgressProvider> progressProvider, - TransactionPool transactionPool, - Transitions transitions, - @ShellMainThread ShellExecutor executor) { - if (progressProvider.isPresent()) { - return Optional.of( - new UnfoldTransitionHandler(progressProvider.get(), transactionPool, executor, - transitions)); + @DynamicOverride Lazy<Optional<UnfoldTransitionHandler>> handler) { + if (progressProvider.isPresent() + && progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) { + return handler.get(); } return Optional.empty(); } @@ -561,29 +558,6 @@ public abstract class WMShellBaseModule { return Optional.empty(); } - // Legacy split (optional feature) - - @WMSingleton - @Provides - static Optional<LegacySplitScreen> provideLegacySplitScreen( - Optional<LegacySplitScreenController> splitScreenController) { - return splitScreenController.map((controller) -> controller.asLegacySplitScreen()); - } - - @BindsOptionalOf - abstract LegacySplitScreenController optionalLegacySplitScreenController(); - - // App Pairs (optional feature) - - @WMSingleton - @Provides - static Optional<AppPairs> provideAppPairs(Optional<AppPairsController> appPairsController) { - return appPairsController.map((controller) -> controller.asAppPairs()); - } - - @BindsOptionalOf - abstract AppPairsController optionalAppPairs(); - // // Starting window // @@ -664,7 +638,6 @@ public abstract class WMShellBaseModule { KidsModeTaskOrganizer kidsModeTaskOrganizer, Optional<BubbleController> bubblesOptional, Optional<SplitScreenController> splitScreenOptional, - Optional<AppPairsController> appPairsOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, FullscreenTaskListener fullscreenTaskListener, Optional<FullscreenUnfoldController> appUnfoldTransitionController, @@ -682,7 +655,6 @@ public abstract class WMShellBaseModule { kidsModeTaskOrganizer, bubblesOptional, splitScreenOptional, - appPairsOptional, pipTouchHandlerOptional, fullscreenTaskListener, appUnfoldTransitionController, @@ -709,17 +681,15 @@ public abstract class WMShellBaseModule { static ShellCommandHandlerImpl provideShellCommandHandlerImpl( ShellTaskOrganizer shellTaskOrganizer, KidsModeTaskOrganizer kidsModeTaskOrganizer, - Optional<LegacySplitScreenController> legacySplitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<Pip> pipOptional, Optional<OneHandedController> oneHandedOptional, Optional<HideDisplayCutoutController> hideDisplayCutout, - Optional<AppPairsController> appPairsOptional, Optional<RecentTasksController> recentTasksOptional, @ShellMainThread ShellExecutor mainExecutor) { return new ShellCommandHandlerImpl(shellTaskOrganizer, kidsModeTaskOrganizer, - legacySplitScreenOptional, splitScreenOptional, pipOptional, oneHandedOptional, - hideDisplayCutout, appPairsOptional, recentTasksOptional, mainExecutor); + splitScreenOptional, pipOptional, oneHandedOptional, hideDisplayCutout, + recentTasksOptional, mainExecutor); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index b3799e2cf8d9..47c9214bcae9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -16,7 +16,6 @@ package com.android.wm.shell.dagger; -import android.animation.AnimationHandler; import android.content.Context; import android.content.pm.LauncherApps; import android.os.Handler; @@ -31,7 +30,6 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TaskViewTransitions; import com.android.wm.shell.WindowManagerShellWrapper; -import com.android.wm.shell.apppairs.AppPairsController; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -43,13 +41,11 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.common.annotations.ChoreographerSfVsync; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.fullscreen.FullscreenUnfoldController; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; @@ -67,6 +63,7 @@ import com.android.wm.shell.pip.PipTransitionState; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.pip.phone.PipController; +import com.android.wm.shell.pip.phone.PipKeepClearAlgorithm; import com.android.wm.shell.pip.phone.PipMotionHelper; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.recents.RecentTasksController; @@ -75,6 +72,8 @@ import com.android.wm.shell.splitscreen.StageTaskUnfoldController; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import com.android.wm.shell.unfold.UnfoldBackgroundController; +import com.android.wm.shell.unfold.UnfoldTransitionHandler; +import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator; import java.util.Optional; @@ -182,31 +181,6 @@ public class WMShellModule { recentTasks, stageTaskUnfoldControllerProvider); } - @WMSingleton - @Provides - static LegacySplitScreenController provideLegacySplitScreen(Context context, - DisplayController displayController, SystemWindows systemWindows, - DisplayImeController displayImeController, TransactionPool transactionPool, - ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, - TaskStackListenerImpl taskStackListener, Transitions transitions, - @ShellMainThread ShellExecutor mainExecutor, - @ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) { - return new LegacySplitScreenController(context, displayController, systemWindows, - displayImeController, transactionPool, shellTaskOrganizer, syncQueue, - taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler); - } - - @WMSingleton - @Provides - static AppPairsController provideAppPairs(ShellTaskOrganizer shellTaskOrganizer, - SyncTransactionQueue syncQueue, DisplayController displayController, - @ShellMainThread ShellExecutor mainExecutor, - DisplayImeController displayImeController, - DisplayInsetsController displayInsetsController) { - return new AppPairsController(shellTaskOrganizer, syncQueue, displayController, - mainExecutor, displayImeController, displayInsetsController); - } - // // Pip // @@ -215,7 +189,8 @@ public class WMShellModule { @Provides static Optional<Pip> providePip(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, - PipBoundsState pipBoundsState, PipMediaController pipMediaController, + PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState, + PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController, WindowManagerShellWrapper windowManagerShellWrapper, @@ -224,7 +199,8 @@ public class WMShellModule { Optional<OneHandedController> oneHandedController, @ShellMainThread ShellExecutor mainExecutor) { return Optional.ofNullable(PipController.create(context, displayController, - pipAppOpsListener, pipBoundsAlgorithm, pipBoundsState, + pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, + pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController, windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder, oneHandedController, mainExecutor)); @@ -244,6 +220,12 @@ public class WMShellModule { @WMSingleton @Provides + static PipKeepClearAlgorithm providePipKeepClearAlgorithm() { + return new PipKeepClearAlgorithm(); + } + + @WMSingleton + @Provides static PipBoundsAlgorithm providesPipBoundsAlgorithm(Context context, PipBoundsState pipBoundsState, PipSnapAlgorithm pipSnapAlgorithm) { return new PipBoundsAlgorithm(context, pipBoundsState, pipSnapAlgorithm); @@ -356,15 +338,37 @@ public class WMShellModule { @Provides @DynamicOverride static FullscreenUnfoldController provideFullscreenUnfoldController( - Context context, Optional<ShellUnfoldProgressProvider> progressProvider, - Lazy<UnfoldBackgroundController> unfoldBackgroundController, - DisplayInsetsController displayInsetsController, + Optional<UnfoldTransitionHandler> unfoldTransitionHandler, + FullscreenUnfoldTaskAnimator fullscreenUnfoldTaskAnimator, + UnfoldBackgroundController unfoldBackgroundController, @ShellMainThread ShellExecutor mainExecutor ) { - return new FullscreenUnfoldController(context, mainExecutor, - unfoldBackgroundController.get(), progressProvider.get(), - displayInsetsController); + return new FullscreenUnfoldController(mainExecutor, + unfoldBackgroundController, progressProvider.get(), + unfoldTransitionHandler.get(), fullscreenUnfoldTaskAnimator); + } + + @Provides + static FullscreenUnfoldTaskAnimator provideFullscreenUnfoldTaskAnimator( + Context context, + DisplayInsetsController displayInsetsController + ) { + return new FullscreenUnfoldTaskAnimator(context, displayInsetsController); + } + + @WMSingleton + @Provides + @DynamicOverride + static UnfoldTransitionHandler provideUnfoldTransitionHandler( + Optional<ShellUnfoldProgressProvider> progressProvider, + FullscreenUnfoldTaskAnimator animator, + UnfoldBackgroundController backgroundController, + TransactionPool transactionPool, + Transitions transitions, + @ShellMainThread ShellExecutor executor) { + return new UnfoldTransitionHandler(progressProvider.get(), animator, + transactionPool, backgroundController, executor, transitions); } @Provides diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java index 73e6cba43ec0..1fc1215b6cea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java @@ -76,6 +76,8 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { taskInfo.taskId); final Point positionInParent = taskInfo.positionInParent; mDataByTaskId.put(taskInfo.taskId, new TaskData(leash, positionInParent)); + mAnimatableTasksListener.onTaskAppeared(taskInfo); + if (Transitions.ENABLE_SHELL_TRANSITIONS) return; mSyncQueue.runInSync(t -> { // Reset several properties back to fullscreen (PiP, for example, leaves all these @@ -87,15 +89,15 @@ public class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { t.show(leash); }); - mAnimatableTasksListener.onTaskAppeared(taskInfo); updateRecentsForVisibleFullscreenTask(taskInfo); } @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { + mAnimatableTasksListener.onTaskInfoChanged(taskInfo); + if (Transitions.ENABLE_SHELL_TRANSITIONS) return; - mAnimatableTasksListener.onTaskInfoChanged(taskInfo); updateRecentsForVisibleFullscreenTask(taskInfo); final TaskData data = mDataByTaskId.get(taskInfo.taskId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java index aa3868cfca84..99f15f65f74e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenUnfoldController.java @@ -17,213 +17,119 @@ package com.android.wm.shell.fullscreen; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.util.MathUtils.lerp; -import static android.view.Display.DEFAULT_DISPLAY; -import android.animation.RectEvaluator; -import android.animation.TypeEvaluator; import android.annotation.NonNull; import android.app.ActivityManager; -import android.app.TaskInfo; -import android.content.Context; -import android.graphics.Matrix; -import android.graphics.Rect; -import android.util.SparseArray; -import android.view.InsetsSource; -import android.view.InsetsState; import android.view.SurfaceControl; -import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener; import com.android.wm.shell.unfold.UnfoldBackgroundController; +import com.android.wm.shell.unfold.UnfoldTransitionHandler; +import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator; import java.util.concurrent.Executor; /** * Controls full screen app unfold transition: animating cropping window and scaling when * folding or unfolding a foldable device. + * + * - When Shell transitions are disabled (legacy mode) this controller animates task surfaces + * when doing both fold and unfold. + * + * - When Shell transitions are enabled this controller animates the surfaces only when + * folding a foldable device. It's not done as a shell transition because we are not committed + * to the display size WM changes yet. + * In this case unfolding is handled by + * {@link com.android.wm.shell.unfold.UnfoldTransitionHandler}. */ -public final class FullscreenUnfoldController implements UnfoldListener, - OnInsetsChangedListener { - - private static final float[] FLOAT_9 = new float[9]; - private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect()); - - private static final float HORIZONTAL_START_MARGIN = 0.08f; - private static final float VERTICAL_START_MARGIN = 0.03f; - private static final float END_SCALE = 1f; - private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2; +public final class FullscreenUnfoldController implements UnfoldListener { private final Executor mExecutor; private final ShellUnfoldProgressProvider mProgressProvider; - private final DisplayInsetsController mDisplayInsetsController; - - private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>(); private final UnfoldBackgroundController mBackgroundController; - - private InsetsSource mTaskbarInsetsSource; - - private final float mWindowCornerRadiusPx; - private final float mExpandedTaskBarHeight; - private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); + private final FullscreenUnfoldTaskAnimator mAnimator; + private final UnfoldTransitionHandler mUnfoldTransitionHandler; + + private boolean mShouldHandleAnimation = false; public FullscreenUnfoldController( - @NonNull Context context, @NonNull Executor executor, @NonNull UnfoldBackgroundController backgroundController, @NonNull ShellUnfoldProgressProvider progressProvider, - @NonNull DisplayInsetsController displayInsetsController + @NonNull UnfoldTransitionHandler unfoldTransitionHandler, + @NonNull FullscreenUnfoldTaskAnimator animator ) { mExecutor = executor; mProgressProvider = progressProvider; - mDisplayInsetsController = displayInsetsController; - mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context); - mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.taskbar_frame_height); mBackgroundController = backgroundController; + mUnfoldTransitionHandler = unfoldTransitionHandler; + mAnimator = animator; } /** * Initializes the controller */ public void init() { + mAnimator.init(); mProgressProvider.addListener(mExecutor, this); - mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this); + } + + @Override + public void onStateChangeStarted() { + mShouldHandleAnimation = !mUnfoldTransitionHandler.willHandleTransition(); } @Override public void onStateChangeProgress(float progress) { - if (mAnimationContextByTaskId.size() == 0) return; + if (!mAnimator.hasActiveTasks() || !mShouldHandleAnimation) return; mBackgroundController.ensureBackground(mTransaction); - - for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { - final AnimationContext context = mAnimationContextByTaskId.valueAt(i); - - context.mCurrentCropRect.set(RECT_EVALUATOR - .evaluate(progress, context.mStartCropRect, context.mEndCropRect)); - - float scale = lerp(START_SCALE, END_SCALE, progress); - context.mMatrix.setScale(scale, scale, context.mCurrentCropRect.exactCenterX(), - context.mCurrentCropRect.exactCenterY()); - - mTransaction.setWindowCrop(context.mLeash, context.mCurrentCropRect) - .setMatrix(context.mLeash, context.mMatrix, FLOAT_9) - .setCornerRadius(context.mLeash, mWindowCornerRadiusPx); - } - + mAnimator.applyAnimationProgress(progress, mTransaction); mTransaction.apply(); } @Override public void onStateChangeFinished() { - for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { - final AnimationContext context = mAnimationContextByTaskId.valueAt(i); - resetSurface(context); + if (!mShouldHandleAnimation) { + return; } + mShouldHandleAnimation = false; + mAnimator.resetAllSurfaces(mTransaction); mBackgroundController.removeBackground(mTransaction); mTransaction.apply(); } - @Override - public void insetsChanged(InsetsState insetsState) { - mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); - for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { - AnimationContext context = mAnimationContextByTaskId.valueAt(i); - context.update(mTaskbarInsetsSource, context.mTaskInfo); - } - } - /** * Called when a new matching task appeared */ public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource, - taskInfo); - mAnimationContextByTaskId.put(taskInfo.taskId, animationContext); + mAnimator.addTask(taskInfo, leash); } /** * Called when matching task changed */ public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId); - if (animationContext != null) { - animationContext.update(mTaskbarInsetsSource, taskInfo); - } + mAnimator.onTaskInfoChanged(taskInfo); } /** * Called when matching task vanished */ public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId); - if (animationContext != null) { - // PiP task has its own cleanup path, ignore surface reset to avoid conflict. - if (taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED) { - resetSurface(animationContext); - } - mAnimationContextByTaskId.remove(taskInfo.taskId); + // PiP task has its own cleanup path, ignore surface reset to avoid conflict. + if (taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED) { + mAnimator.resetSurface(taskInfo, mTransaction); } + mAnimator.removeTask(taskInfo); - if (mAnimationContextByTaskId.size() == 0) { + if (!mAnimator.hasActiveTasks()) { mBackgroundController.removeBackground(mTransaction); } mTransaction.apply(); } - - private void resetSurface(AnimationContext context) { - mTransaction - .setWindowCrop(context.mLeash, null) - .setCornerRadius(context.mLeash, 0.0F) - .setMatrix(context.mLeash, 1.0F, 0.0F, 0.0F, 1.0F) - .setPosition(context.mLeash, - (float) context.mTaskInfo.positionInParent.x, - (float) context.mTaskInfo.positionInParent.y); - } - - private class AnimationContext { - final SurfaceControl mLeash; - final Rect mStartCropRect = new Rect(); - final Rect mEndCropRect = new Rect(); - final Rect mCurrentCropRect = new Rect(); - final Matrix mMatrix = new Matrix(); - - TaskInfo mTaskInfo; - - private AnimationContext(SurfaceControl leash, - InsetsSource taskBarInsetsSource, - TaskInfo taskInfo) { - this.mLeash = leash; - update(taskBarInsetsSource, taskInfo); - } - - private void update(InsetsSource taskBarInsetsSource, TaskInfo taskInfo) { - mTaskInfo = taskInfo; - mStartCropRect.set(mTaskInfo.getConfiguration().windowConfiguration.getBounds()); - - if (taskBarInsetsSource != null) { - // Only insets the cropping window with task bar when it's expanded - if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { - mStartCropRect.inset(taskBarInsetsSource - .calculateVisibleInsets(mStartCropRect)); - } - } - - mEndCropRect.set(mStartCropRect); - - int horizontalMargin = (int) (mEndCropRect.width() * HORIZONTAL_START_MARGIN); - mStartCropRect.left = mEndCropRect.left + horizontalMargin; - mStartCropRect.right = mEndCropRect.right - horizontalMargin; - int verticalMargin = (int) (mEndCropRect.height() * VERTICAL_START_MARGIN); - mStartCropRect.top = mEndCropRect.top + verticalMargin; - mStartCropRect.bottom = mEndCropRect.bottom - verticalMargin; - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java deleted file mode 100644 index aced072c8c71..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerImeController.java +++ /dev/null @@ -1,418 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.legacysplitscreen; - -import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED; -import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.annotation.Nullable; -import android.graphics.Rect; -import android.util.Slog; -import android.view.Choreographer; -import android.view.SurfaceControl; -import android.window.TaskOrganizer; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.TransactionPool; - -class DividerImeController implements DisplayImeController.ImePositionProcessor { - private static final String TAG = "DividerImeController"; - private static final boolean DEBUG = LegacySplitScreenController.DEBUG; - - private static final float ADJUSTED_NONFOCUS_DIM = 0.3f; - - private final LegacySplitScreenTaskListener mSplits; - private final TransactionPool mTransactionPool; - private final ShellExecutor mMainExecutor; - private final TaskOrganizer mTaskOrganizer; - - /** - * These are the y positions of the top of the IME surface when it is hidden and when it is - * shown respectively. These are NOT necessarily the top of the visible IME itself. - */ - private int mHiddenTop = 0; - private int mShownTop = 0; - - // The following are target states (what we are curretly animating towards). - /** - * {@code true} if, at the end of the animation, the split task positions should be - * adjusted by height of the IME. This happens when the secondary split is the IME target. - */ - private boolean mTargetAdjusted = false; - /** - * {@code true} if, at the end of the animation, the IME should be shown/visible - * regardless of what has focus. - */ - private boolean mTargetShown = false; - private float mTargetPrimaryDim = 0.f; - private float mTargetSecondaryDim = 0.f; - - // The following are the current (most recent) states set during animation - /** {@code true} if the secondary split has IME focus. */ - private boolean mSecondaryHasFocus = false; - /** The dimming currently applied to the primary/secondary splits. */ - private float mLastPrimaryDim = 0.f; - private float mLastSecondaryDim = 0.f; - /** The most recent y position of the top of the IME surface */ - private int mLastAdjustTop = -1; - - // The following are states reached last time an animation fully completed. - /** {@code true} if the IME was shown/visible by the last-completed animation. */ - private boolean mImeWasShown = false; - /** {@code true} if the split positions were adjusted by the last-completed animation. */ - private boolean mAdjusted = false; - - /** - * When some aspect of split-screen needs to animate independent from the IME, - * this will be non-null and control split animation. - */ - @Nullable - private ValueAnimator mAnimation = null; - - private boolean mPaused = true; - private boolean mPausedTargetAdjusted = false; - - DividerImeController(LegacySplitScreenTaskListener splits, TransactionPool pool, - ShellExecutor mainExecutor, TaskOrganizer taskOrganizer) { - mSplits = splits; - mTransactionPool = pool; - mMainExecutor = mainExecutor; - mTaskOrganizer = taskOrganizer; - } - - private DividerView getView() { - return mSplits.mSplitScreenController.getDividerView(); - } - - private LegacySplitDisplayLayout getLayout() { - return mSplits.mSplitScreenController.getSplitLayout(); - } - - private boolean isDividerHidden() { - final DividerView view = mSplits.mSplitScreenController.getDividerView(); - return view == null || view.isHidden(); - } - - private boolean getSecondaryHasFocus(int displayId) { - WindowContainerToken imeSplit = mTaskOrganizer.getImeTarget(displayId); - return imeSplit != null - && (imeSplit.asBinder() == mSplits.mSecondary.token.asBinder()); - } - - void reset() { - mPaused = true; - mPausedTargetAdjusted = false; - mAnimation = null; - mAdjusted = mTargetAdjusted = false; - mImeWasShown = mTargetShown = false; - mTargetPrimaryDim = mTargetSecondaryDim = mLastPrimaryDim = mLastSecondaryDim = 0.f; - mSecondaryHasFocus = false; - mLastAdjustTop = -1; - } - - private void updateDimTargets() { - final boolean splitIsVisible = !getView().isHidden(); - mTargetPrimaryDim = (mSecondaryHasFocus && mTargetShown && splitIsVisible) - ? ADJUSTED_NONFOCUS_DIM : 0.f; - mTargetSecondaryDim = (!mSecondaryHasFocus && mTargetShown && splitIsVisible) - ? ADJUSTED_NONFOCUS_DIM : 0.f; - } - - - @Override - public void onImeControlTargetChanged(int displayId, boolean controlling) { - // Restore the split layout when wm-shell is not controlling IME insets anymore. - if (!controlling && mTargetShown) { - mPaused = false; - mTargetAdjusted = mTargetShown = false; - mTargetPrimaryDim = mTargetSecondaryDim = 0.f; - updateImeAdjustState(true /* force */); - startAsyncAnimation(); - } - } - - @Override - @ImeAnimationFlags - public int onImeStartPositioning(int displayId, int hiddenTop, int shownTop, - boolean imeShouldShow, boolean imeIsFloating, SurfaceControl.Transaction t) { - if (isDividerHidden()) { - return 0; - } - mHiddenTop = hiddenTop; - mShownTop = shownTop; - mTargetShown = imeShouldShow; - mSecondaryHasFocus = getSecondaryHasFocus(displayId); - final boolean targetAdjusted = imeShouldShow && mSecondaryHasFocus - && !imeIsFloating && !getLayout().mDisplayLayout.isLandscape() - && !mSplits.mSplitScreenController.isMinimized(); - if (mLastAdjustTop < 0) { - mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop; - } else if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) { - if (mTargetAdjusted != targetAdjusted && targetAdjusted == mAdjusted) { - // Check for an "interruption" of an existing animation. In this case, we - // need to fake-flip the last-known state direction so that the animation - // completes in the other direction. - mAdjusted = mTargetAdjusted; - } else if (targetAdjusted && mTargetAdjusted && mAdjusted) { - // Already fully adjusted for IME, but IME height has changed; so, force-start - // an async animation to the new IME height. - mAdjusted = false; - } - } - if (mPaused) { - mPausedTargetAdjusted = targetAdjusted; - if (DEBUG) Slog.d(TAG, " ime starting but paused " + dumpState()); - return (targetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0; - } - mTargetAdjusted = targetAdjusted; - updateDimTargets(); - if (DEBUG) Slog.d(TAG, " ime starting. " + dumpState()); - if (mAnimation != null || (mImeWasShown && imeShouldShow - && mTargetAdjusted != mAdjusted)) { - // We need to animate adjustment independently of the IME position, so - // start our own animation to drive adjustment. This happens when a - // different split's editor has gained focus while the IME is still visible. - startAsyncAnimation(); - } - updateImeAdjustState(); - - return (mTargetAdjusted || mAdjusted) ? IME_ANIMATION_NO_ALPHA : 0; - } - - private void updateImeAdjustState() { - updateImeAdjustState(false /* force */); - } - - private void updateImeAdjustState(boolean force) { - if (mAdjusted != mTargetAdjusted || force) { - // Reposition the server's secondary split position so that it evaluates - // insets properly. - WindowContainerTransaction wct = new WindowContainerTransaction(); - final LegacySplitDisplayLayout splitLayout = getLayout(); - if (mTargetAdjusted) { - splitLayout.updateAdjustedBounds(mShownTop, mHiddenTop, mShownTop); - wct.setBounds(mSplits.mSecondary.token, splitLayout.mAdjustedSecondary); - // "Freeze" the configuration size so that the app doesn't get a config - // or relaunch. This is required because normally nav-bar contributes - // to configuration bounds (via nondecorframe). - Rect adjustAppBounds = new Rect(mSplits.mSecondary.configuration - .windowConfiguration.getAppBounds()); - adjustAppBounds.offset(0, splitLayout.mAdjustedSecondary.top - - splitLayout.mSecondary.top); - wct.setAppBounds(mSplits.mSecondary.token, adjustAppBounds); - wct.setScreenSizeDp(mSplits.mSecondary.token, - mSplits.mSecondary.configuration.screenWidthDp, - mSplits.mSecondary.configuration.screenHeightDp); - - wct.setBounds(mSplits.mPrimary.token, splitLayout.mAdjustedPrimary); - adjustAppBounds = new Rect(mSplits.mPrimary.configuration - .windowConfiguration.getAppBounds()); - adjustAppBounds.offset(0, splitLayout.mAdjustedPrimary.top - - splitLayout.mPrimary.top); - wct.setAppBounds(mSplits.mPrimary.token, adjustAppBounds); - wct.setScreenSizeDp(mSplits.mPrimary.token, - mSplits.mPrimary.configuration.screenWidthDp, - mSplits.mPrimary.configuration.screenHeightDp); - } else { - wct.setBounds(mSplits.mSecondary.token, splitLayout.mSecondary); - wct.setAppBounds(mSplits.mSecondary.token, null); - wct.setScreenSizeDp(mSplits.mSecondary.token, - SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); - wct.setBounds(mSplits.mPrimary.token, splitLayout.mPrimary); - wct.setAppBounds(mSplits.mPrimary.token, null); - wct.setScreenSizeDp(mSplits.mPrimary.token, - SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); - } - - if (!mSplits.mSplitScreenController.getWmProxy().queueSyncTransactionIfWaiting(wct)) { - mTaskOrganizer.applyTransaction(wct); - } - } - - // Update all the adjusted-for-ime states - if (!mPaused) { - final DividerView view = getView(); - if (view != null) { - view.setAdjustedForIme(mTargetShown, mTargetShown - ? DisplayImeController.ANIMATION_DURATION_SHOW_MS - : DisplayImeController.ANIMATION_DURATION_HIDE_MS); - } - } - mSplits.mSplitScreenController.setAdjustedForIme(mTargetShown && !mPaused); - } - - @Override - public void onImePositionChanged(int displayId, int imeTop, - SurfaceControl.Transaction t) { - if (mAnimation != null || isDividerHidden() || mPaused) { - // Not synchronized with IME anymore, so return. - return; - } - final float fraction = ((float) imeTop - mHiddenTop) / (mShownTop - mHiddenTop); - final float progress = mTargetShown ? fraction : 1.f - fraction; - onProgress(progress, t); - } - - @Override - public void onImeEndPositioning(int displayId, boolean cancelled, - SurfaceControl.Transaction t) { - if (mAnimation != null || isDividerHidden() || mPaused) { - // Not synchronized with IME anymore, so return. - return; - } - onEnd(cancelled, t); - } - - private void onProgress(float progress, SurfaceControl.Transaction t) { - final DividerView view = getView(); - if (mTargetAdjusted != mAdjusted && !mPaused) { - final LegacySplitDisplayLayout splitLayout = getLayout(); - final float fraction = mTargetAdjusted ? progress : 1.f - progress; - mLastAdjustTop = (int) (fraction * mShownTop + (1.f - fraction) * mHiddenTop); - splitLayout.updateAdjustedBounds(mLastAdjustTop, mHiddenTop, mShownTop); - view.resizeSplitSurfaces(t, splitLayout.mAdjustedPrimary, - splitLayout.mAdjustedSecondary); - } - final float invProg = 1.f - progress; - view.setResizeDimLayer(t, true /* primary */, - mLastPrimaryDim * invProg + progress * mTargetPrimaryDim); - view.setResizeDimLayer(t, false /* primary */, - mLastSecondaryDim * invProg + progress * mTargetSecondaryDim); - } - - void setDimsHidden(SurfaceControl.Transaction t, boolean hidden) { - final DividerView view = getView(); - if (hidden) { - view.setResizeDimLayer(t, true /* primary */, 0.f /* alpha */); - view.setResizeDimLayer(t, false /* primary */, 0.f /* alpha */); - } else { - updateDimTargets(); - view.setResizeDimLayer(t, true /* primary */, mTargetPrimaryDim); - view.setResizeDimLayer(t, false /* primary */, mTargetSecondaryDim); - } - } - - private void onEnd(boolean cancelled, SurfaceControl.Transaction t) { - if (!cancelled) { - onProgress(1.f, t); - mAdjusted = mTargetAdjusted; - mImeWasShown = mTargetShown; - mLastAdjustTop = mAdjusted ? mShownTop : mHiddenTop; - mLastPrimaryDim = mTargetPrimaryDim; - mLastSecondaryDim = mTargetSecondaryDim; - } - } - - private void startAsyncAnimation() { - if (mAnimation != null) { - mAnimation.cancel(); - } - mAnimation = ValueAnimator.ofFloat(0.f, 1.f); - mAnimation.setDuration(DisplayImeController.ANIMATION_DURATION_SHOW_MS); - if (mTargetAdjusted != mAdjusted) { - final float fraction = - ((float) mLastAdjustTop - mHiddenTop) / (mShownTop - mHiddenTop); - final float progress = mTargetAdjusted ? fraction : 1.f - fraction; - mAnimation.setCurrentFraction(progress); - } - - mAnimation.addUpdateListener(animation -> { - SurfaceControl.Transaction t = mTransactionPool.acquire(); - float value = (float) animation.getAnimatedValue(); - onProgress(value, t); - t.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); - t.apply(); - mTransactionPool.release(t); - }); - mAnimation.setInterpolator(DisplayImeController.INTERPOLATOR); - mAnimation.addListener(new AnimatorListenerAdapter() { - private boolean mCancel = false; - - @Override - public void onAnimationCancel(Animator animation) { - mCancel = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - SurfaceControl.Transaction t = mTransactionPool.acquire(); - onEnd(mCancel, t); - t.apply(); - mTransactionPool.release(t); - mAnimation = null; - } - }); - mAnimation.start(); - } - - private String dumpState() { - return "top:" + mHiddenTop + "->" + mShownTop - + " adj:" + mAdjusted + "->" + mTargetAdjusted + "(" + mLastAdjustTop + ")" - + " shw:" + mImeWasShown + "->" + mTargetShown - + " dims:" + mLastPrimaryDim + "," + mLastSecondaryDim - + "->" + mTargetPrimaryDim + "," + mTargetSecondaryDim - + " shf:" + mSecondaryHasFocus + " desync:" + (mAnimation != null) - + " paus:" + mPaused + "[" + mPausedTargetAdjusted + "]"; - } - - /** Completely aborts/resets adjustment state */ - public void pause(int displayId) { - if (DEBUG) Slog.d(TAG, "ime pause posting " + dumpState()); - mMainExecutor.execute(() -> { - if (DEBUG) Slog.d(TAG, "ime pause run posted " + dumpState()); - if (mPaused) { - return; - } - mPaused = true; - mPausedTargetAdjusted = mTargetAdjusted; - mTargetAdjusted = false; - mTargetPrimaryDim = mTargetSecondaryDim = 0.f; - updateImeAdjustState(); - startAsyncAnimation(); - if (mAnimation != null) { - mAnimation.end(); - } - }); - } - - public void resume(int displayId) { - if (DEBUG) Slog.d(TAG, "ime resume posting " + dumpState()); - mMainExecutor.execute(() -> { - if (DEBUG) Slog.d(TAG, "ime resume run posted " + dumpState()); - if (!mPaused) { - return; - } - mPaused = false; - mTargetAdjusted = mPausedTargetAdjusted; - updateDimTargets(); - final DividerView view = getView(); - if ((mTargetAdjusted != mAdjusted) && !mSplits.mSplitScreenController.isMinimized() - && view != null) { - // End unminimize animations since they conflict with adjustment animations. - view.finishAnimations(); - } - updateImeAdjustState(); - startAsyncAnimation(); - }); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java deleted file mode 100644 index 73be2835d2cd..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerView.java +++ /dev/null @@ -1,1314 +0,0 @@ -/* - * 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.wm.shell.legacysplitscreen; - -import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW; -import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW; -import static android.view.WindowManager.DOCKED_RIGHT; - -import static com.android.wm.shell.animation.Interpolators.DIM_INTERPOLATOR; -import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR; -import static com.android.wm.shell.common.split.DividerView.TOUCH_ANIMATION_DURATION; -import static com.android.wm.shell.common.split.DividerView.TOUCH_RELEASE_ANIMATION_DURATION; - -import android.animation.AnimationHandler; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.Matrix; -import android.graphics.Rect; -import android.graphics.Region; -import android.graphics.Region.Op; -import android.hardware.display.DisplayManager; -import android.os.Bundle; -import android.util.AttributeSet; -import android.util.Slog; -import android.view.Choreographer; -import android.view.Display; -import android.view.MotionEvent; -import android.view.PointerIcon; -import android.view.SurfaceControl; -import android.view.SurfaceControl.Transaction; -import android.view.VelocityTracker; -import android.view.View; -import android.view.View.OnTouchListener; -import android.view.ViewConfiguration; -import android.view.ViewTreeObserver.InternalInsetsInfo; -import android.view.ViewTreeObserver.OnComputeInternalInsetsListener; -import android.view.WindowManager; -import android.view.accessibility.AccessibilityNodeInfo; -import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; -import android.view.animation.Interpolator; -import android.view.animation.PathInterpolator; -import android.widget.FrameLayout; - -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.policy.DividerSnapAlgorithm; -import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; -import com.android.internal.policy.DockedDividerUtils; -import com.android.wm.shell.R; -import com.android.wm.shell.animation.FlingAnimationUtils; -import com.android.wm.shell.animation.Interpolators; -import com.android.wm.shell.common.split.DividerHandleView; - -import java.util.function.Consumer; - -/** - * Docked stack divider. - */ -public class DividerView extends FrameLayout implements OnTouchListener, - OnComputeInternalInsetsListener { - private static final String TAG = "DividerView"; - private static final boolean DEBUG = LegacySplitScreenController.DEBUG; - - interface DividerCallbacks { - void onDraggingStart(); - void onDraggingEnd(); - } - - public static final int INVALID_RECENTS_GROW_TARGET = -1; - - private static final int LOG_VALUE_RESIZE_50_50 = 0; - private static final int LOG_VALUE_RESIZE_DOCKED_SMALLER = 1; - private static final int LOG_VALUE_RESIZE_DOCKED_LARGER = 2; - - private static final int LOG_VALUE_UNDOCK_MAX_DOCKED = 0; - private static final int LOG_VALUE_UNDOCK_MAX_OTHER = 1; - - private static final int TASK_POSITION_SAME = Integer.MAX_VALUE; - - /** - * How much the background gets scaled when we are in the minimized dock state. - */ - private static final float MINIMIZE_DOCK_SCALE = 0f; - private static final float ADJUSTED_FOR_IME_SCALE = 0.5f; - - private static final Interpolator IME_ADJUST_INTERPOLATOR = - new PathInterpolator(0.2f, 0f, 0.1f, 1f); - - private DividerHandleView mHandle; - private View mBackground; - private MinimizedDockShadow mMinimizedShadow; - private int mStartX; - private int mStartY; - private int mStartPosition; - private int mDockSide; - private boolean mMoving; - private int mTouchSlop; - private boolean mBackgroundLifted; - private boolean mIsInMinimizeInteraction; - SnapTarget mSnapTargetBeforeMinimized; - - private int mDividerInsets; - private final Display mDefaultDisplay; - - private int mDividerSize; - private int mTouchElevation; - private int mLongPressEntraceAnimDuration; - - private final Rect mDockedRect = new Rect(); - private final Rect mDockedTaskRect = new Rect(); - private final Rect mOtherTaskRect = new Rect(); - private final Rect mOtherRect = new Rect(); - private final Rect mDockedInsetRect = new Rect(); - private final Rect mOtherInsetRect = new Rect(); - private final Rect mLastResizeRect = new Rect(); - private final Rect mTmpRect = new Rect(); - private LegacySplitScreenController mSplitScreenController; - private WindowManagerProxy mWindowManagerProxy; - private DividerWindowManager mWindowManager; - private VelocityTracker mVelocityTracker; - private FlingAnimationUtils mFlingAnimationUtils; - private LegacySplitDisplayLayout mSplitLayout; - private DividerImeController mImeController; - private DividerCallbacks mCallback; - - private AnimationHandler mSfVsyncAnimationHandler; - private ValueAnimator mCurrentAnimator; - private boolean mEntranceAnimationRunning; - private boolean mExitAnimationRunning; - private int mExitStartPosition; - private boolean mDockedStackMinimized; - private boolean mHomeStackResizable; - private boolean mAdjustedForIme; - private DividerState mState; - - private LegacySplitScreenTaskListener mTiles; - boolean mFirstLayout = true; - int mDividerPositionX; - int mDividerPositionY; - - private final Matrix mTmpMatrix = new Matrix(); - private final float[] mTmpValues = new float[9]; - - // The view is removed or in the process of been removed from the system. - private boolean mRemoved; - - // Whether the surface for this view has been hidden regardless of actual visibility. This is - // used interact with keyguard. - private boolean mSurfaceHidden = false; - - private final AccessibilityDelegate mHandleDelegate = new AccessibilityDelegate() { - @Override - public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(host, info); - final DividerSnapAlgorithm snapAlgorithm = getSnapAlgorithm(); - if (isHorizontalDivision()) { - info.addAction(new AccessibilityAction(R.id.action_move_tl_full, - mContext.getString(R.string.accessibility_action_divider_top_full))); - if (snapAlgorithm.isFirstSplitTargetAvailable()) { - info.addAction(new AccessibilityAction(R.id.action_move_tl_70, - mContext.getString(R.string.accessibility_action_divider_top_70))); - } - if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) { - // Only show the middle target if there are more than 1 split target - info.addAction(new AccessibilityAction(R.id.action_move_tl_50, - mContext.getString(R.string.accessibility_action_divider_top_50))); - } - if (snapAlgorithm.isLastSplitTargetAvailable()) { - info.addAction(new AccessibilityAction(R.id.action_move_tl_30, - mContext.getString(R.string.accessibility_action_divider_top_30))); - } - info.addAction(new AccessibilityAction(R.id.action_move_rb_full, - mContext.getString(R.string.accessibility_action_divider_bottom_full))); - } else { - info.addAction(new AccessibilityAction(R.id.action_move_tl_full, - mContext.getString(R.string.accessibility_action_divider_left_full))); - if (snapAlgorithm.isFirstSplitTargetAvailable()) { - info.addAction(new AccessibilityAction(R.id.action_move_tl_70, - mContext.getString(R.string.accessibility_action_divider_left_70))); - } - if (snapAlgorithm.showMiddleSplitTargetForAccessibility()) { - // Only show the middle target if there are more than 1 split target - info.addAction(new AccessibilityAction(R.id.action_move_tl_50, - mContext.getString(R.string.accessibility_action_divider_left_50))); - } - if (snapAlgorithm.isLastSplitTargetAvailable()) { - info.addAction(new AccessibilityAction(R.id.action_move_tl_30, - mContext.getString(R.string.accessibility_action_divider_left_30))); - } - info.addAction(new AccessibilityAction(R.id.action_move_rb_full, - mContext.getString(R.string.accessibility_action_divider_right_full))); - } - } - - @Override - public boolean performAccessibilityAction(View host, int action, Bundle args) { - int currentPosition = getCurrentPosition(); - SnapTarget nextTarget = null; - DividerSnapAlgorithm snapAlgorithm = mSplitLayout.getSnapAlgorithm(); - if (action == R.id.action_move_tl_full) { - nextTarget = snapAlgorithm.getDismissEndTarget(); - } else if (action == R.id.action_move_tl_70) { - nextTarget = snapAlgorithm.getLastSplitTarget(); - } else if (action == R.id.action_move_tl_50) { - nextTarget = snapAlgorithm.getMiddleTarget(); - } else if (action == R.id.action_move_tl_30) { - nextTarget = snapAlgorithm.getFirstSplitTarget(); - } else if (action == R.id.action_move_rb_full) { - nextTarget = snapAlgorithm.getDismissStartTarget(); - } - if (nextTarget != null) { - startDragging(true /* animate */, false /* touching */); - stopDragging(currentPosition, nextTarget, 250, Interpolators.FAST_OUT_SLOW_IN); - return true; - } - return super.performAccessibilityAction(host, action, args); - } - }; - - private final Runnable mResetBackgroundRunnable = new Runnable() { - @Override - public void run() { - resetBackground(); - } - }; - - public DividerView(Context context) { - this(context, null); - } - - public DividerView(Context context, @Nullable AttributeSet attrs) { - this(context, attrs, 0); - } - - public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { - this(context, attrs, defStyleAttr, 0); - } - - public DividerView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, - int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - final DisplayManager displayManager = - (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); - mDefaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY); - } - - public void setAnimationHandler(AnimationHandler sfVsyncAnimationHandler) { - mSfVsyncAnimationHandler = sfVsyncAnimationHandler; - } - - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - mHandle = findViewById(R.id.docked_divider_handle); - mBackground = findViewById(R.id.docked_divider_background); - mMinimizedShadow = findViewById(R.id.minimized_dock_shadow); - mHandle.setOnTouchListener(this); - final int dividerWindowWidth = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_thickness); - mDividerInsets = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_insets); - mDividerSize = dividerWindowWidth - 2 * mDividerInsets; - mTouchElevation = getResources().getDimensionPixelSize( - R.dimen.docked_stack_divider_lift_elevation); - mLongPressEntraceAnimDuration = getResources().getInteger( - R.integer.long_press_dock_anim_duration); - mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); - mFlingAnimationUtils = new FlingAnimationUtils(getResources().getDisplayMetrics(), 0.3f); - boolean landscape = getResources().getConfiguration().orientation - == Configuration.ORIENTATION_LANDSCAPE; - mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(), - landscape ? TYPE_HORIZONTAL_DOUBLE_ARROW : TYPE_VERTICAL_DOUBLE_ARROW)); - getViewTreeObserver().addOnComputeInternalInsetsListener(this); - mHandle.setAccessibilityDelegate(mHandleDelegate); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - // Save the current target if not minimized once attached to window - if (mDockSide != WindowManager.DOCKED_INVALID && !mIsInMinimizeInteraction) { - saveSnapTargetBeforeMinimized(mSnapTargetBeforeMinimized); - } - mFirstLayout = true; - } - - void onDividerRemoved() { - mRemoved = true; - mCallback = null; - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - if (mFirstLayout) { - // Wait for first layout so that the ViewRootImpl surface has been created. - initializeSurfaceState(); - mFirstLayout = false; - } - int minimizeLeft = 0; - int minimizeTop = 0; - if (mDockSide == WindowManager.DOCKED_TOP) { - minimizeTop = mBackground.getTop(); - } else if (mDockSide == WindowManager.DOCKED_LEFT) { - minimizeLeft = mBackground.getLeft(); - } else if (mDockSide == WindowManager.DOCKED_RIGHT) { - minimizeLeft = mBackground.getRight() - mMinimizedShadow.getWidth(); - } - mMinimizedShadow.layout(minimizeLeft, minimizeTop, - minimizeLeft + mMinimizedShadow.getMeasuredWidth(), - minimizeTop + mMinimizedShadow.getMeasuredHeight()); - if (changed) { - notifySplitScreenBoundsChanged(); - } - } - - void injectDependencies(LegacySplitScreenController splitScreenController, - DividerWindowManager windowManager, DividerState dividerState, - DividerCallbacks callback, LegacySplitScreenTaskListener tiles, - LegacySplitDisplayLayout sdl, DividerImeController imeController, - WindowManagerProxy wmProxy) { - mSplitScreenController = splitScreenController; - mWindowManager = windowManager; - mState = dividerState; - mCallback = callback; - mTiles = tiles; - mSplitLayout = sdl; - mImeController = imeController; - mWindowManagerProxy = wmProxy; - - if (mState.mRatioPositionBeforeMinimized == 0) { - // Set the middle target as the initial state - mSnapTargetBeforeMinimized = mSplitLayout.getSnapAlgorithm().getMiddleTarget(); - } else { - repositionSnapTargetBeforeMinimized(); - } - } - - /** Gets non-minimized secondary bounds of split screen. */ - public Rect getNonMinimizedSplitScreenSecondaryBounds() { - mOtherTaskRect.set(mSplitLayout.mSecondary); - return mOtherTaskRect; - } - - private boolean inSplitMode() { - return getVisibility() == VISIBLE; - } - - /** Unlike setVisible, this directly hides the surface without changing view visibility. */ - void setHidden(boolean hidden) { - if (mSurfaceHidden == hidden) { - return; - } - mSurfaceHidden = hidden; - post(() -> { - final SurfaceControl sc = getWindowSurfaceControl(); - if (sc == null) { - return; - } - Transaction t = mTiles.getTransaction(); - if (hidden) { - t.hide(sc); - } else { - t.show(sc); - } - mImeController.setDimsHidden(t, hidden); - t.apply(); - mTiles.releaseTransaction(t); - }); - } - - boolean isHidden() { - return getVisibility() != View.VISIBLE || mSurfaceHidden; - } - - /** Starts dragging the divider bar. */ - public boolean startDragging(boolean animate, boolean touching) { - cancelFlingAnimation(); - if (touching) { - mHandle.setTouching(true, animate); - } - mDockSide = mSplitLayout.getPrimarySplitSide(); - - mWindowManagerProxy.setResizing(true); - if (touching) { - mWindowManager.setSlippery(false); - liftBackground(); - } - if (mCallback != null) { - mCallback.onDraggingStart(); - } - return inSplitMode(); - } - - /** Stops dragging the divider bar. */ - public void stopDragging(int position, float velocity, boolean avoidDismissStart, - boolean logMetrics) { - mHandle.setTouching(false, true /* animate */); - fling(position, velocity, avoidDismissStart, logMetrics); - mWindowManager.setSlippery(true); - releaseBackground(); - } - - private void stopDragging(int position, SnapTarget target, long duration, - Interpolator interpolator) { - stopDragging(position, target, duration, 0 /* startDelay*/, 0 /* endDelay */, interpolator); - } - - private void stopDragging(int position, SnapTarget target, long duration, - Interpolator interpolator, long endDelay) { - stopDragging(position, target, duration, 0 /* startDelay*/, endDelay, interpolator); - } - - private void stopDragging(int position, SnapTarget target, long duration, long startDelay, - long endDelay, Interpolator interpolator) { - mHandle.setTouching(false, true /* animate */); - flingTo(position, target, duration, startDelay, endDelay, interpolator); - mWindowManager.setSlippery(true); - releaseBackground(); - } - - private void stopDragging() { - mHandle.setTouching(false, true /* animate */); - mWindowManager.setSlippery(true); - mWindowManagerProxy.setResizing(false); - releaseBackground(); - } - - private void updateDockSide() { - mDockSide = mSplitLayout.getPrimarySplitSide(); - mMinimizedShadow.setDockSide(mDockSide); - } - - public DividerSnapAlgorithm getSnapAlgorithm() { - return mDockedStackMinimized ? mSplitLayout.getMinimizedSnapAlgorithm(mHomeStackResizable) - : mSplitLayout.getSnapAlgorithm(); - } - - public int getCurrentPosition() { - return isHorizontalDivision() ? mDividerPositionY : mDividerPositionX; - } - - public boolean isMinimized() { - return mDockedStackMinimized; - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - convertToScreenCoordinates(event); - final int action = event.getAction() & MotionEvent.ACTION_MASK; - switch (action) { - case MotionEvent.ACTION_DOWN: - mVelocityTracker = VelocityTracker.obtain(); - mVelocityTracker.addMovement(event); - mStartX = (int) event.getX(); - mStartY = (int) event.getY(); - boolean result = startDragging(true /* animate */, true /* touching */); - if (!result) { - - // Weren't able to start dragging successfully, so cancel it again. - stopDragging(); - } - mStartPosition = getCurrentPosition(); - mMoving = false; - return result; - case MotionEvent.ACTION_MOVE: - mVelocityTracker.addMovement(event); - int x = (int) event.getX(); - int y = (int) event.getY(); - boolean exceededTouchSlop = - isHorizontalDivision() && Math.abs(y - mStartY) > mTouchSlop - || (!isHorizontalDivision() && Math.abs(x - mStartX) > mTouchSlop); - if (!mMoving && exceededTouchSlop) { - mStartX = x; - mStartY = y; - mMoving = true; - } - if (mMoving && mDockSide != WindowManager.DOCKED_INVALID) { - SnapTarget snapTarget = getSnapAlgorithm().calculateSnapTarget( - mStartPosition, 0 /* velocity */, false /* hardDismiss */); - resizeStackSurfaces(calculatePosition(x, y), mStartPosition, snapTarget, - null /* transaction */); - } - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - if (!mMoving) { - stopDragging(); - break; - } - - x = (int) event.getRawX(); - y = (int) event.getRawY(); - mVelocityTracker.addMovement(event); - mVelocityTracker.computeCurrentVelocity(1000); - int position = calculatePosition(x, y); - stopDragging(position, isHorizontalDivision() ? mVelocityTracker.getYVelocity() - : mVelocityTracker.getXVelocity(), false /* avoidDismissStart */, - true /* log */); - mMoving = false; - break; - } - return true; - } - - private void logResizeEvent(SnapTarget snapTarget) { - if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissStartTarget()) { - MetricsLogger.action( - mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideTopLeft(mDockSide) - ? LOG_VALUE_UNDOCK_MAX_OTHER - : LOG_VALUE_UNDOCK_MAX_DOCKED); - } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissEndTarget()) { - MetricsLogger.action( - mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideBottomRight(mDockSide) - ? LOG_VALUE_UNDOCK_MAX_OTHER - : LOG_VALUE_UNDOCK_MAX_DOCKED); - } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getMiddleTarget()) { - MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE, - LOG_VALUE_RESIZE_50_50); - } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getFirstSplitTarget()) { - MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE, - dockSideTopLeft(mDockSide) - ? LOG_VALUE_RESIZE_DOCKED_SMALLER - : LOG_VALUE_RESIZE_DOCKED_LARGER); - } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getLastSplitTarget()) { - MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE, - dockSideTopLeft(mDockSide) - ? LOG_VALUE_RESIZE_DOCKED_LARGER - : LOG_VALUE_RESIZE_DOCKED_SMALLER); - } - } - - private void convertToScreenCoordinates(MotionEvent event) { - event.setLocation(event.getRawX(), event.getRawY()); - } - - private void fling(int position, float velocity, boolean avoidDismissStart, - boolean logMetrics) { - DividerSnapAlgorithm currentSnapAlgorithm = getSnapAlgorithm(); - SnapTarget snapTarget = currentSnapAlgorithm.calculateSnapTarget(position, velocity); - if (avoidDismissStart && snapTarget == currentSnapAlgorithm.getDismissStartTarget()) { - snapTarget = currentSnapAlgorithm.getFirstSplitTarget(); - } - if (logMetrics) { - logResizeEvent(snapTarget); - } - ValueAnimator anim = getFlingAnimator(position, snapTarget, 0 /* endDelay */); - mFlingAnimationUtils.apply(anim, position, snapTarget.position, velocity); - anim.start(); - } - - private void flingTo(int position, SnapTarget target, long duration, long startDelay, - long endDelay, Interpolator interpolator) { - ValueAnimator anim = getFlingAnimator(position, target, endDelay); - anim.setDuration(duration); - anim.setStartDelay(startDelay); - anim.setInterpolator(interpolator); - anim.start(); - } - - private ValueAnimator getFlingAnimator(int position, final SnapTarget snapTarget, - final long endDelay) { - if (mCurrentAnimator != null) { - cancelFlingAnimation(); - updateDockSide(); - } - if (DEBUG) Slog.d(TAG, "Getting fling " + position + "->" + snapTarget.position); - final boolean taskPositionSameAtEnd = snapTarget.flag == SnapTarget.FLAG_NONE; - ValueAnimator anim = ValueAnimator.ofInt(position, snapTarget.position); - anim.addUpdateListener(animation -> resizeStackSurfaces((int) animation.getAnimatedValue(), - taskPositionSameAtEnd && animation.getAnimatedFraction() == 1f - ? TASK_POSITION_SAME - : snapTarget.taskPosition, - snapTarget, null /* transaction */)); - Consumer<Boolean> endAction = cancelled -> { - if (DEBUG) Slog.d(TAG, "End Fling " + cancelled + " min:" + mIsInMinimizeInteraction); - final boolean wasMinimizeInteraction = mIsInMinimizeInteraction; - // Reset minimized divider position after unminimized state animation finishes. - if (!cancelled && !mDockedStackMinimized && mIsInMinimizeInteraction) { - mIsInMinimizeInteraction = false; - } - boolean dismissed = commitSnapFlags(snapTarget); - mWindowManagerProxy.setResizing(false); - updateDockSide(); - mCurrentAnimator = null; - mEntranceAnimationRunning = false; - mExitAnimationRunning = false; - if (!dismissed && !wasMinimizeInteraction) { - mWindowManagerProxy.applyResizeSplits(snapTarget.position, mSplitLayout); - } - if (mCallback != null) { - mCallback.onDraggingEnd(); - } - - // Record last snap target the divider moved to - if (!mIsInMinimizeInteraction) { - // The last snapTarget position can be negative when the last divider position was - // offscreen. In that case, save the middle (default) SnapTarget so calculating next - // position isn't negative. - final SnapTarget saveTarget; - if (snapTarget.position < 0) { - saveTarget = mSplitLayout.getSnapAlgorithm().getMiddleTarget(); - } else { - saveTarget = snapTarget; - } - final DividerSnapAlgorithm snapAlgo = mSplitLayout.getSnapAlgorithm(); - if (saveTarget.position != snapAlgo.getDismissEndTarget().position - && saveTarget.position != snapAlgo.getDismissStartTarget().position) { - saveSnapTargetBeforeMinimized(saveTarget); - } - } - notifySplitScreenBoundsChanged(); - }; - anim.addListener(new AnimatorListenerAdapter() { - - private boolean mCancelled; - - @Override - public void onAnimationCancel(Animator animation) { - mCancelled = true; - } - - @Override - public void onAnimationEnd(Animator animation) { - long delay = 0; - if (endDelay != 0) { - delay = endDelay; - } else if (mCancelled) { - delay = 0; - } - if (delay == 0) { - endAction.accept(mCancelled); - } else { - final Boolean cancelled = mCancelled; - if (DEBUG) Slog.d(TAG, "Posting endFling " + cancelled + " d:" + delay + "ms"); - getHandler().postDelayed(() -> endAction.accept(cancelled), delay); - } - } - }); - mCurrentAnimator = anim; - mCurrentAnimator.setAnimationHandler(mSfVsyncAnimationHandler); - return anim; - } - - private void notifySplitScreenBoundsChanged() { - if (mSplitLayout.mPrimary == null || mSplitLayout.mSecondary == null) { - return; - } - mOtherTaskRect.set(mSplitLayout.mSecondary); - - mTmpRect.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), mHandle.getBottom()); - if (isHorizontalDivision()) { - mTmpRect.offsetTo(mHandle.getLeft(), mDividerPositionY); - } else { - mTmpRect.offsetTo(mDividerPositionX, mHandle.getTop()); - } - mWindowManagerProxy.setTouchRegion(mTmpRect); - - mTmpRect.set(mSplitLayout.mDisplayLayout.stableInsets()); - switch (mSplitLayout.getPrimarySplitSide()) { - case WindowManager.DOCKED_LEFT: - mTmpRect.left = 0; - break; - case WindowManager.DOCKED_RIGHT: - mTmpRect.right = 0; - break; - case WindowManager.DOCKED_TOP: - mTmpRect.top = 0; - break; - } - mSplitScreenController.notifyBoundsChanged(mOtherTaskRect, mTmpRect); - } - - private void cancelFlingAnimation() { - if (mCurrentAnimator != null) { - mCurrentAnimator.cancel(); - } - } - - private boolean commitSnapFlags(SnapTarget target) { - if (target.flag == SnapTarget.FLAG_NONE) { - return false; - } - final boolean dismissOrMaximize; - if (target.flag == SnapTarget.FLAG_DISMISS_START) { - dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT - || mDockSide == WindowManager.DOCKED_TOP; - } else { - dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT - || mDockSide == WindowManager.DOCKED_BOTTOM; - } - mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, mSplitLayout, dismissOrMaximize); - Transaction t = mTiles.getTransaction(); - setResizeDimLayer(t, true /* primary */, 0f); - setResizeDimLayer(t, false /* primary */, 0f); - t.apply(); - mTiles.releaseTransaction(t); - return true; - } - - private void liftBackground() { - if (mBackgroundLifted) { - return; - } - if (isHorizontalDivision()) { - mBackground.animate().scaleY(1.4f); - } else { - mBackground.animate().scaleX(1.4f); - } - mBackground.animate() - .setInterpolator(Interpolators.TOUCH_RESPONSE) - .setDuration(TOUCH_ANIMATION_DURATION) - .translationZ(mTouchElevation) - .start(); - - // Lift handle as well so it doesn't get behind the background, even though it doesn't - // cast shadow. - mHandle.animate() - .setInterpolator(Interpolators.TOUCH_RESPONSE) - .setDuration(TOUCH_ANIMATION_DURATION) - .translationZ(mTouchElevation) - .start(); - mBackgroundLifted = true; - } - - private void releaseBackground() { - if (!mBackgroundLifted) { - return; - } - mBackground.animate() - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) - .translationZ(0) - .scaleX(1f) - .scaleY(1f) - .start(); - mHandle.animate() - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .setDuration(TOUCH_RELEASE_ANIMATION_DURATION) - .translationZ(0) - .start(); - mBackgroundLifted = false; - } - - private void initializeSurfaceState() { - int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; - // Recalculate the split-layout's internal tile bounds - mSplitLayout.resizeSplits(midPos); - Transaction t = mTiles.getTransaction(); - if (mDockedStackMinimized) { - int position = mSplitLayout.getMinimizedSnapAlgorithm(mHomeStackResizable) - .getMiddleTarget().position; - calculateBoundsForPosition(position, mDockSide, mDockedRect); - calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide), - mOtherRect); - mDividerPositionX = mDividerPositionY = position; - resizeSplitSurfaces(t, mDockedRect, mSplitLayout.mPrimary, - mOtherRect, mSplitLayout.mSecondary); - } else { - resizeSplitSurfaces(t, mSplitLayout.mPrimary, null, - mSplitLayout.mSecondary, null); - } - setResizeDimLayer(t, true /* primary */, 0.f /* alpha */); - setResizeDimLayer(t, false /* secondary */, 0.f /* alpha */); - t.apply(); - mTiles.releaseTransaction(t); - - // Get the actually-visible bar dimensions (relative to full window). This is a thin - // bar going through the center. - final Rect dividerBar = isHorizontalDivision() - ? new Rect(0, mDividerInsets, mSplitLayout.mDisplayLayout.width(), - mDividerInsets + mDividerSize) - : new Rect(mDividerInsets, 0, mDividerInsets + mDividerSize, - mSplitLayout.mDisplayLayout.height()); - final Region touchRegion = new Region(dividerBar); - // Add in the "draggable" portion. While not visible, this is an expanded area that the - // user can interact with. - touchRegion.union(new Rect(mHandle.getLeft(), mHandle.getTop(), - mHandle.getRight(), mHandle.getBottom())); - mWindowManager.setTouchRegion(touchRegion); - } - - void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable, - Transaction t) { - mHomeStackResizable = isHomeStackResizable; - updateDockSide(); - if (!minimized) { - resetBackground(); - } - mMinimizedShadow.setAlpha(minimized ? 1f : 0f); - if (mDockedStackMinimized != minimized) { - mDockedStackMinimized = minimized; - if (mSplitLayout.mDisplayLayout.rotation() != mDefaultDisplay.getRotation()) { - // Splitscreen to minimize is about to starts after rotating landscape to seascape, - // update display info and snap algorithm targets - repositionSnapTargetBeforeMinimized(); - } - if (mIsInMinimizeInteraction != minimized || mCurrentAnimator != null) { - cancelFlingAnimation(); - if (minimized) { - // Relayout to recalculate the divider shadow when minimizing - requestLayout(); - mIsInMinimizeInteraction = true; - resizeStackSurfaces(mSplitLayout.getMinimizedSnapAlgorithm(mHomeStackResizable) - .getMiddleTarget(), t); - } else { - resizeStackSurfaces(mSnapTargetBeforeMinimized, t); - mIsInMinimizeInteraction = false; - } - } - } - } - - void enterSplitMode(boolean isHomeStackResizable) { - setHidden(false); - - SnapTarget miniMid = - mSplitLayout.getMinimizedSnapAlgorithm(isHomeStackResizable).getMiddleTarget(); - if (mDockedStackMinimized) { - mDividerPositionY = mDividerPositionX = miniMid.position; - } - } - - /** - * Tries to grab a surface control from ViewRootImpl. If this isn't available for some reason - * (ie. the window isn't ready yet), it will get the surfacecontrol that the WindowlessWM has - * assigned to it. - */ - private SurfaceControl getWindowSurfaceControl() { - return mWindowManager.mSystemWindows.getViewSurface(this); - } - - void exitSplitMode() { - // The view is going to be removed right after this function involved, updates the surface - // in the current thread instead of posting it to the view's UI thread. - final SurfaceControl sc = getWindowSurfaceControl(); - if (sc == null) { - return; - } - Transaction t = mTiles.getTransaction(); - t.hide(sc); - mImeController.setDimsHidden(t, true); - t.apply(); - mTiles.releaseTransaction(t); - - // Reset tile bounds - int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; - mWindowManagerProxy.applyResizeSplits(midPos, mSplitLayout); - } - - void setMinimizedDockStack(boolean minimized, long animDuration, - boolean isHomeStackResizable) { - if (DEBUG) Slog.d(TAG, "setMinDock: " + mDockedStackMinimized + "->" + minimized); - mHomeStackResizable = isHomeStackResizable; - updateDockSide(); - if (mDockedStackMinimized != minimized) { - mIsInMinimizeInteraction = true; - mDockedStackMinimized = minimized; - stopDragging(minimized - ? mSnapTargetBeforeMinimized.position - : getCurrentPosition(), - minimized - ? mSplitLayout.getMinimizedSnapAlgorithm(mHomeStackResizable) - .getMiddleTarget() - : mSnapTargetBeforeMinimized, - animDuration, Interpolators.FAST_OUT_SLOW_IN, 0); - setAdjustedForIme(false, animDuration); - } - if (!minimized) { - mBackground.animate().withEndAction(mResetBackgroundRunnable); - } - mBackground.animate() - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .setDuration(animDuration) - .start(); - } - - // Needed to end any currently playing animations when they might compete with other anims - // (specifically, IME adjust animation immediately after leaving minimized). Someday maybe - // these can be unified, but not today. - void finishAnimations() { - if (mCurrentAnimator != null) { - mCurrentAnimator.end(); - } - } - - void setAdjustedForIme(boolean adjustedForIme, long animDuration) { - if (mAdjustedForIme == adjustedForIme) { - return; - } - updateDockSide(); - mHandle.animate() - .setInterpolator(IME_ADJUST_INTERPOLATOR) - .setDuration(animDuration) - .alpha(adjustedForIme ? 0f : 1f) - .start(); - if (mDockSide == WindowManager.DOCKED_TOP) { - mBackground.setPivotY(0); - mBackground.animate() - .scaleY(adjustedForIme ? ADJUSTED_FOR_IME_SCALE : 1f); - } - if (!adjustedForIme) { - mBackground.animate().withEndAction(mResetBackgroundRunnable); - } - mBackground.animate() - .setInterpolator(IME_ADJUST_INTERPOLATOR) - .setDuration(animDuration) - .start(); - mAdjustedForIme = adjustedForIme; - } - - private void saveSnapTargetBeforeMinimized(SnapTarget target) { - mSnapTargetBeforeMinimized = target; - mState.mRatioPositionBeforeMinimized = (float) target.position - / (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height() - : mSplitLayout.mDisplayLayout.width()); - } - - private void resetBackground() { - mBackground.setPivotX(mBackground.getWidth() / 2); - mBackground.setPivotY(mBackground.getHeight() / 2); - mBackground.setScaleX(1f); - mBackground.setScaleY(1f); - mMinimizedShadow.setAlpha(0f); - } - - @Override - protected void onConfigurationChanged(Configuration newConfig) { - super.onConfigurationChanged(newConfig); - } - - private void repositionSnapTargetBeforeMinimized() { - int position = (int) (mState.mRatioPositionBeforeMinimized - * (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height() - : mSplitLayout.mDisplayLayout.width())); - - // Set the snap target before minimized but do not save until divider is attached and not - // minimized because it does not know its minimized state yet. - mSnapTargetBeforeMinimized = - mSplitLayout.getSnapAlgorithm().calculateNonDismissingSnapTarget(position); - } - - private int calculatePosition(int touchX, int touchY) { - return isHorizontalDivision() ? calculateYPosition(touchY) : calculateXPosition(touchX); - } - - public boolean isHorizontalDivision() { - return getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT; - } - - private int calculateXPosition(int touchX) { - return mStartPosition + touchX - mStartX; - } - - private int calculateYPosition(int touchY) { - return mStartPosition + touchY - mStartY; - } - - private void alignTopLeft(Rect containingRect, Rect rect) { - int width = rect.width(); - int height = rect.height(); - rect.set(containingRect.left, containingRect.top, - containingRect.left + width, containingRect.top + height); - } - - private void alignBottomRight(Rect containingRect, Rect rect) { - int width = rect.width(); - int height = rect.height(); - rect.set(containingRect.right - width, containingRect.bottom - height, - containingRect.right, containingRect.bottom); - } - - private void calculateBoundsForPosition(int position, int dockSide, Rect outRect) { - DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, - mSplitLayout.mDisplayLayout.width(), mSplitLayout.mDisplayLayout.height(), - mDividerSize); - } - - private void resizeStackSurfaces(SnapTarget taskSnapTarget, Transaction t) { - resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget, t); - } - - void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) { - resizeSplitSurfaces(t, dockedRect, null, otherRect, null); - } - - private void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect dockedTaskRect, - Rect otherRect, Rect otherTaskRect) { - dockedTaskRect = dockedTaskRect == null ? dockedRect : dockedTaskRect; - otherTaskRect = otherTaskRect == null ? otherRect : otherTaskRect; - - mDividerPositionX = mSplitLayout.getPrimarySplitSide() == DOCKED_RIGHT - ? otherRect.right : dockedRect.right; - mDividerPositionY = dockedRect.bottom; - - if (DEBUG) { - Slog.d(TAG, "Resizing split surfaces: " + dockedRect + " " + dockedTaskRect - + " " + otherRect + " " + otherTaskRect); - } - - t.setPosition(mTiles.mPrimarySurface, dockedTaskRect.left, dockedTaskRect.top); - Rect crop = new Rect(dockedRect); - crop.offsetTo(-Math.min(dockedTaskRect.left - dockedRect.left, 0), - -Math.min(dockedTaskRect.top - dockedRect.top, 0)); - t.setWindowCrop(mTiles.mPrimarySurface, crop); - t.setPosition(mTiles.mSecondarySurface, otherTaskRect.left, otherTaskRect.top); - crop.set(otherRect); - crop.offsetTo(-(otherTaskRect.left - otherRect.left), - -(otherTaskRect.top - otherRect.top)); - t.setWindowCrop(mTiles.mSecondarySurface, crop); - final SurfaceControl dividerCtrl = getWindowSurfaceControl(); - if (dividerCtrl != null) { - if (isHorizontalDivision()) { - t.setPosition(dividerCtrl, 0, mDividerPositionY - mDividerInsets); - } else { - t.setPosition(dividerCtrl, mDividerPositionX - mDividerInsets, 0); - } - } - } - - void setResizeDimLayer(Transaction t, boolean primary, float alpha) { - SurfaceControl dim = primary ? mTiles.mPrimaryDim : mTiles.mSecondaryDim; - if (alpha <= 0.001f) { - t.hide(dim); - } else { - t.setAlpha(dim, alpha); - t.show(dim); - } - } - - void resizeStackSurfaces(int position, int taskPosition, SnapTarget taskSnapTarget, - Transaction transaction) { - if (mRemoved) { - // This divider view has been removed so shouldn't have any additional influence. - return; - } - calculateBoundsForPosition(position, mDockSide, mDockedRect); - calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide), - mOtherRect); - - if (mDockedRect.equals(mLastResizeRect) && !mEntranceAnimationRunning) { - return; - } - - // Make sure shadows are updated - if (mBackground.getZ() > 0f) { - mBackground.invalidate(); - } - - final boolean ownTransaction = transaction == null; - final Transaction t = ownTransaction ? mTiles.getTransaction() : transaction; - mLastResizeRect.set(mDockedRect); - if (mIsInMinimizeInteraction) { - calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide, - mDockedTaskRect); - calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, - DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect); - - // Move a right-docked-app to line up with the divider while dragging it - if (mDockSide == DOCKED_RIGHT) { - mDockedTaskRect.offset(Math.max(position, -mDividerSize) - - mDockedTaskRect.left + mDividerSize, 0); - } - resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect); - if (ownTransaction) { - t.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId()); - t.apply(); - mTiles.releaseTransaction(t); - } - return; - } - - if (mEntranceAnimationRunning && taskPosition != TASK_POSITION_SAME) { - calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect); - - // Move a docked app if from the right in position with the divider up to insets - if (mDockSide == DOCKED_RIGHT) { - mDockedTaskRect.offset(Math.max(position, -mDividerSize) - - mDockedTaskRect.left + mDividerSize, 0); - } - calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide), - mOtherTaskRect); - resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect); - } else if (mExitAnimationRunning && taskPosition != TASK_POSITION_SAME) { - calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect); - mDockedInsetRect.set(mDockedTaskRect); - calculateBoundsForPosition(mExitStartPosition, - DockedDividerUtils.invertDockSide(mDockSide), mOtherTaskRect); - mOtherInsetRect.set(mOtherTaskRect); - applyExitAnimationParallax(mOtherTaskRect, position); - - // Move a right-docked-app to line up with the divider while dragging it - if (mDockSide == DOCKED_RIGHT) { - mDockedTaskRect.offset(position + mDividerSize, 0); - } - resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect); - } else if (taskPosition != TASK_POSITION_SAME) { - calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide), - mOtherRect); - int dockSideInverted = DockedDividerUtils.invertDockSide(mDockSide); - int taskPositionDocked = - restrictDismissingTaskPosition(taskPosition, mDockSide, taskSnapTarget); - int taskPositionOther = - restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget); - calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect); - calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect); - mTmpRect.set(0, 0, mSplitLayout.mDisplayLayout.width(), - mSplitLayout.mDisplayLayout.height()); - alignTopLeft(mDockedRect, mDockedTaskRect); - alignTopLeft(mOtherRect, mOtherTaskRect); - mDockedInsetRect.set(mDockedTaskRect); - mOtherInsetRect.set(mOtherTaskRect); - if (dockSideTopLeft(mDockSide)) { - alignTopLeft(mTmpRect, mDockedInsetRect); - alignBottomRight(mTmpRect, mOtherInsetRect); - } else { - alignBottomRight(mTmpRect, mDockedInsetRect); - alignTopLeft(mTmpRect, mOtherInsetRect); - } - applyDismissingParallax(mDockedTaskRect, mDockSide, taskSnapTarget, position, - taskPositionDocked); - applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position, - taskPositionOther); - resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect); - } else { - resizeSplitSurfaces(t, mDockedRect, null, mOtherRect, null); - } - SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position); - float dimFraction = getDimFraction(position, closestDismissTarget); - setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction); - if (ownTransaction) { - t.apply(); - mTiles.releaseTransaction(t); - } - } - - private void applyExitAnimationParallax(Rect taskRect, int position) { - if (mDockSide == WindowManager.DOCKED_TOP) { - taskRect.offset(0, (int) ((position - mExitStartPosition) * 0.25f)); - } else if (mDockSide == WindowManager.DOCKED_LEFT) { - taskRect.offset((int) ((position - mExitStartPosition) * 0.25f), 0); - } else if (mDockSide == WindowManager.DOCKED_RIGHT) { - taskRect.offset((int) ((mExitStartPosition - position) * 0.25f), 0); - } - } - - private float getDimFraction(int position, SnapTarget dismissTarget) { - if (mEntranceAnimationRunning) { - return 0f; - } - float fraction = getSnapAlgorithm().calculateDismissingFraction(position); - fraction = Math.max(0, Math.min(fraction, 1f)); - fraction = DIM_INTERPOLATOR.getInterpolation(fraction); - return fraction; - } - - /** - * When the snap target is dismissing one side, make sure that the dismissing side doesn't get - * 0 size. - */ - private int restrictDismissingTaskPosition(int taskPosition, int dockSide, - SnapTarget snapTarget) { - if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) { - return Math.max(mSplitLayout.getSnapAlgorithm().getFirstSplitTarget().position, - mStartPosition); - } else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END - && dockSideBottomRight(dockSide)) { - return Math.min(mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position, - mStartPosition); - } else { - return taskPosition; - } - } - - /** - * Applies a parallax to the task when dismissing. - */ - private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget, - int position, int taskPosition) { - float fraction = Math.min(1, Math.max(0, - mSplitLayout.getSnapAlgorithm().calculateDismissingFraction(position))); - SnapTarget dismissTarget = null; - SnapTarget splitTarget = null; - int start = 0; - if (position <= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position - && dockSideTopLeft(dockSide)) { - dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissStartTarget(); - splitTarget = mSplitLayout.getSnapAlgorithm().getFirstSplitTarget(); - start = taskPosition; - } else if (position >= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position - && dockSideBottomRight(dockSide)) { - dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissEndTarget(); - splitTarget = mSplitLayout.getSnapAlgorithm().getLastSplitTarget(); - start = splitTarget.position; - } - if (dismissTarget != null && fraction > 0f - && isDismissing(splitTarget, position, dockSide)) { - fraction = calculateParallaxDismissingFraction(fraction, dockSide); - int offsetPosition = (int) (start + fraction - * (dismissTarget.position - splitTarget.position)); - int width = taskRect.width(); - int height = taskRect.height(); - switch (dockSide) { - case WindowManager.DOCKED_LEFT: - taskRect.left = offsetPosition - width; - taskRect.right = offsetPosition; - break; - case WindowManager.DOCKED_RIGHT: - taskRect.left = offsetPosition + mDividerSize; - taskRect.right = offsetPosition + width + mDividerSize; - break; - case WindowManager.DOCKED_TOP: - taskRect.top = offsetPosition - height; - taskRect.bottom = offsetPosition; - break; - case WindowManager.DOCKED_BOTTOM: - taskRect.top = offsetPosition + mDividerSize; - taskRect.bottom = offsetPosition + height + mDividerSize; - break; - } - } - } - - /** - * @return for a specified {@code fraction}, this returns an adjusted value that simulates a - * slowing down parallax effect - */ - private static float calculateParallaxDismissingFraction(float fraction, int dockSide) { - float result = SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f; - - // Less parallax at the top, just because. - if (dockSide == WindowManager.DOCKED_TOP) { - result /= 2f; - } - return result; - } - - private static boolean isDismissing(SnapTarget snapTarget, int position, int dockSide) { - if (dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT) { - return position < snapTarget.position; - } else { - return position > snapTarget.position; - } - } - - private boolean isDismissTargetPrimary(SnapTarget dismissTarget) { - return (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide)) - || (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END - && dockSideBottomRight(mDockSide)); - } - - /** - * @return true if and only if {@code dockSide} is top or left - */ - private static boolean dockSideTopLeft(int dockSide) { - return dockSide == WindowManager.DOCKED_TOP || dockSide == WindowManager.DOCKED_LEFT; - } - - /** - * @return true if and only if {@code dockSide} is bottom or right - */ - private static boolean dockSideBottomRight(int dockSide) { - return dockSide == WindowManager.DOCKED_BOTTOM || dockSide == WindowManager.DOCKED_RIGHT; - } - - @Override - public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) { - inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - inoutInfo.touchableRegion.set(mHandle.getLeft(), mHandle.getTop(), mHandle.getRight(), - mHandle.getBottom()); - inoutInfo.touchableRegion.op(mBackground.getLeft(), mBackground.getTop(), - mBackground.getRight(), mBackground.getBottom(), Op.UNION); - } - - void onUndockingTask() { - int dockSide = mSplitLayout.getPrimarySplitSide(); - if (inSplitMode()) { - startDragging(false /* animate */, false /* touching */); - SnapTarget target = dockSideTopLeft(dockSide) - ? mSplitLayout.getSnapAlgorithm().getDismissEndTarget() - : mSplitLayout.getSnapAlgorithm().getDismissStartTarget(); - - // Don't start immediately - give a little bit time to settle the drag resize change. - mExitAnimationRunning = true; - mExitStartPosition = getCurrentPosition(); - stopDragging(mExitStartPosition, target, 336 /* duration */, 100 /* startDelay */, - 0 /* endDelay */, Interpolators.FAST_OUT_SLOW_IN); - } - } - - private int calculatePositionForInsetBounds() { - mSplitLayout.mDisplayLayout.getStableBounds(mTmpRect); - return DockedDividerUtils.calculatePositionForBounds(mTmpRect, mDockSide, mDividerSize); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerWindowManager.java deleted file mode 100644 index 2c3ae68e4749..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/DividerWindowManager.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.wm.shell.legacysplitscreen; - -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; -import static android.view.WindowManager.LayoutParams.FLAG_SLIPPERY; -import static android.view.WindowManager.LayoutParams.FLAG_SPLIT_TOUCH; -import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; -import static android.view.WindowManager.SHELL_ROOT_LAYER_DIVIDER; - -import android.graphics.PixelFormat; -import android.graphics.Region; -import android.os.Binder; -import android.view.View; -import android.view.WindowManager; - -import com.android.wm.shell.common.SystemWindows; - -/** - * Manages the window parameters of the docked stack divider. - */ -final class DividerWindowManager { - - private static final String WINDOW_TITLE = "DockedStackDivider"; - - final SystemWindows mSystemWindows; - private WindowManager.LayoutParams mLp; - private View mView; - - DividerWindowManager(SystemWindows systemWindows) { - mSystemWindows = systemWindows; - } - - /** Add a divider view */ - void add(View view, int width, int height, int displayId) { - mLp = new WindowManager.LayoutParams( - width, height, TYPE_DOCK_DIVIDER, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL - | FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH | FLAG_SLIPPERY, - PixelFormat.TRANSLUCENT); - mLp.token = new Binder(); - mLp.setTitle(WINDOW_TITLE); - mLp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; - view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); - mSystemWindows.addView(view, mLp, displayId, SHELL_ROOT_LAYER_DIVIDER); - mView = view; - } - - void remove() { - if (mView != null) { - mSystemWindows.removeView(mView); - } - mView = null; - } - - void setSlippery(boolean slippery) { - boolean changed = false; - if (slippery && (mLp.flags & FLAG_SLIPPERY) == 0) { - mLp.flags |= FLAG_SLIPPERY; - changed = true; - } else if (!slippery && (mLp.flags & FLAG_SLIPPERY) != 0) { - mLp.flags &= ~FLAG_SLIPPERY; - changed = true; - } - if (changed) { - mSystemWindows.updateViewLayout(mView, mLp); - } - } - - void setTouchable(boolean touchable) { - if (mView == null) { - return; - } - boolean changed = false; - if (!touchable && (mLp.flags & FLAG_NOT_TOUCHABLE) == 0) { - mLp.flags |= FLAG_NOT_TOUCHABLE; - changed = true; - } else if (touchable && (mLp.flags & FLAG_NOT_TOUCHABLE) != 0) { - mLp.flags &= ~FLAG_NOT_TOUCHABLE; - changed = true; - } - if (changed) { - mSystemWindows.updateViewLayout(mView, mLp); - } - } - - /** Sets the touch region to `touchRegion`. Use null to unset.*/ - void setTouchRegion(Region touchRegion) { - if (mView == null) { - return; - } - mSystemWindows.setTouchableRegion(mView, touchRegion); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivity.java deleted file mode 100644 index 4fe28e630114..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivity.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2016 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.wm.shell.legacysplitscreen; - -import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; -import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN; - -import android.annotation.Nullable; -import android.app.Activity; -import android.app.ActivityManager; -import android.os.Bundle; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnTouchListener; -import android.widget.TextView; - -import com.android.wm.shell.R; - -/** - * Translucent activity that gets started on top of a task in multi-window to inform the user that - * we forced the activity below to be resizable. - * - * Note: This activity runs on the main thread of the process hosting the Shell lib. - */ -public class ForcedResizableInfoActivity extends Activity implements OnTouchListener { - - public static final String EXTRA_FORCED_RESIZEABLE_REASON = "extra_forced_resizeable_reason"; - - private static final long DISMISS_DELAY = 2500; - - private final Runnable mFinishRunnable = new Runnable() { - @Override - public void run() { - finish(); - } - }; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.forced_resizable_activity); - TextView tv = findViewById(com.android.internal.R.id.message); - int reason = getIntent().getIntExtra(EXTRA_FORCED_RESIZEABLE_REASON, -1); - String text; - switch (reason) { - case FORCED_RESIZEABLE_REASON_SPLIT_SCREEN: - text = getString(R.string.dock_forced_resizable); - break; - case FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY: - text = getString(R.string.forced_resizable_secondary_display); - break; - default: - throw new IllegalArgumentException("Unexpected forced resizeable reason: " - + reason); - } - tv.setText(text); - getWindow().setTitle(text); - getWindow().getDecorView().setOnTouchListener(this); - } - - @Override - protected void onStart() { - super.onStart(); - getWindow().getDecorView().postDelayed(mFinishRunnable, DISMISS_DELAY); - } - - @Override - protected void onStop() { - super.onStop(); - finish(); - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - finish(); - return true; - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - finish(); - return true; - } - - @Override - public void finish() { - super.finish(); - overridePendingTransition(0, R.anim.forced_resizable_exit); - } - - @Override - public void setTaskDescription(ActivityManager.TaskDescription taskDescription) { - // Do nothing - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java deleted file mode 100644 index 139544f951ce..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/ForcedResizableInfoActivityController.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2016 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.wm.shell.legacysplitscreen; - - -import static com.android.wm.shell.legacysplitscreen.ForcedResizableInfoActivity.EXTRA_FORCED_RESIZEABLE_REASON; - -import android.app.ActivityOptions; -import android.content.Context; -import android.content.Intent; -import android.os.UserHandle; -import android.util.ArraySet; -import android.widget.Toast; - -import com.android.wm.shell.R; -import com.android.wm.shell.common.ShellExecutor; - -import java.util.function.Consumer; - -/** - * Controller that decides when to show the {@link ForcedResizableInfoActivity}. - */ -final class ForcedResizableInfoActivityController implements DividerView.DividerCallbacks { - - private static final String SELF_PACKAGE_NAME = "com.android.systemui"; - - private static final int TIMEOUT = 1000; - private final Context mContext; - private final ShellExecutor mMainExecutor; - private final ArraySet<PendingTaskRecord> mPendingTasks = new ArraySet<>(); - private final ArraySet<String> mPackagesShownInSession = new ArraySet<>(); - private boolean mDividerDragging; - - private final Runnable mTimeoutRunnable = this::showPending; - - private final Consumer<Boolean> mDockedStackExistsListener = exists -> { - if (!exists) { - mPackagesShownInSession.clear(); - } - }; - - /** Record of force resized task that's pending to be handled. */ - private class PendingTaskRecord { - int mTaskId; - /** - * {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SPLIT_SCREEN} or - * {@link android.app.ITaskStackListener#FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY} - */ - int mReason; - - PendingTaskRecord(int taskId, int reason) { - this.mTaskId = taskId; - this.mReason = reason; - } - } - - ForcedResizableInfoActivityController(Context context, - LegacySplitScreenController splitScreenController, - ShellExecutor mainExecutor) { - mContext = context; - mMainExecutor = mainExecutor; - splitScreenController.registerInSplitScreenListener(mDockedStackExistsListener); - } - - @Override - public void onDraggingStart() { - mDividerDragging = true; - mMainExecutor.removeCallbacks(mTimeoutRunnable); - } - - @Override - public void onDraggingEnd() { - mDividerDragging = false; - showPending(); - } - - void onAppTransitionFinished() { - if (!mDividerDragging) { - showPending(); - } - } - - void activityForcedResizable(String packageName, int taskId, int reason) { - if (debounce(packageName)) { - return; - } - mPendingTasks.add(new PendingTaskRecord(taskId, reason)); - postTimeout(); - } - - void activityDismissingSplitScreen() { - Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, - Toast.LENGTH_SHORT).show(); - } - - void activityLaunchOnSecondaryDisplayFailed() { - Toast.makeText(mContext, R.string.activity_launch_on_secondary_display_failed_text, - Toast.LENGTH_SHORT).show(); - } - - private void showPending() { - mMainExecutor.removeCallbacks(mTimeoutRunnable); - for (int i = mPendingTasks.size() - 1; i >= 0; i--) { - PendingTaskRecord pendingRecord = mPendingTasks.valueAt(i); - Intent intent = new Intent(mContext, ForcedResizableInfoActivity.class); - ActivityOptions options = ActivityOptions.makeBasic(); - options.setLaunchTaskId(pendingRecord.mTaskId); - // Set as task overlay and allow to resume, so that when an app enters split-screen and - // becomes paused, the overlay will still be shown. - options.setTaskOverlay(true, true /* canResume */); - intent.putExtra(EXTRA_FORCED_RESIZEABLE_REASON, pendingRecord.mReason); - mContext.startActivityAsUser(intent, options.toBundle(), UserHandle.CURRENT); - } - mPendingTasks.clear(); - } - - private void postTimeout() { - mMainExecutor.removeCallbacks(mTimeoutRunnable); - mMainExecutor.executeDelayed(mTimeoutRunnable, TIMEOUT); - } - - private boolean debounce(String packageName) { - if (packageName == null) { - return false; - } - - // We launch ForcedResizableInfoActivity into a task that was forced resizable, so that - // triggers another notification. So ignore our own activity. - if (SELF_PACKAGE_NAME.equals(packageName)) { - return true; - } - boolean debounce = mPackagesShownInSession.contains(packageName); - mPackagesShownInSession.add(packageName); - return debounce; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java deleted file mode 100644 index f201634d3d4a..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitDisplayLayout.java +++ /dev/null @@ -1,326 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.legacysplitscreen; - -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; -import static android.util.RotationUtils.rotateBounds; -import static android.view.WindowManager.DOCKED_BOTTOM; -import static android.view.WindowManager.DOCKED_INVALID; -import static android.view.WindowManager.DOCKED_LEFT; -import static android.view.WindowManager.DOCKED_RIGHT; -import static android.view.WindowManager.DOCKED_TOP; - -import android.annotation.NonNull; -import android.content.Context; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.graphics.Rect; -import android.util.TypedValue; -import android.window.WindowContainerTransaction; - -import com.android.internal.policy.DividerSnapAlgorithm; -import com.android.internal.policy.DockedDividerUtils; -import com.android.wm.shell.common.DisplayLayout; - -/** - * Handles split-screen related internal display layout. In general, this represents the - * WM-facing understanding of the splits. - */ -public class LegacySplitDisplayLayout { - /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to - * restrict IME adjustment so that a min portion of top stack remains visible.*/ - private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f; - - private static final int DIVIDER_WIDTH_INACTIVE_DP = 4; - - LegacySplitScreenTaskListener mTiles; - DisplayLayout mDisplayLayout; - Context mContext; - - // Lazy stuff - boolean mResourcesValid = false; - int mDividerSize; - int mDividerSizeInactive; - private DividerSnapAlgorithm mSnapAlgorithm = null; - private DividerSnapAlgorithm mMinimizedSnapAlgorithm = null; - Rect mPrimary = null; - Rect mSecondary = null; - Rect mAdjustedPrimary = null; - Rect mAdjustedSecondary = null; - final Rect mTmpBounds = new Rect(); - - public LegacySplitDisplayLayout(Context ctx, DisplayLayout dl, - LegacySplitScreenTaskListener taskTiles) { - mTiles = taskTiles; - mDisplayLayout = dl; - mContext = ctx; - } - - void rotateTo(int newRotation) { - mDisplayLayout.rotateTo(mContext.getResources(), newRotation); - final Configuration config = new Configuration(); - config.unset(); - config.orientation = mDisplayLayout.getOrientation(); - Rect tmpRect = new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height()); - tmpRect.inset(mDisplayLayout.nonDecorInsets()); - config.windowConfiguration.setAppBounds(tmpRect); - tmpRect.set(0, 0, mDisplayLayout.width(), mDisplayLayout.height()); - tmpRect.inset(mDisplayLayout.stableInsets()); - config.screenWidthDp = (int) (tmpRect.width() / mDisplayLayout.density()); - config.screenHeightDp = (int) (tmpRect.height() / mDisplayLayout.density()); - mContext = mContext.createConfigurationContext(config); - mSnapAlgorithm = null; - mMinimizedSnapAlgorithm = null; - mResourcesValid = false; - } - - private void updateResources() { - if (mResourcesValid) { - return; - } - mResourcesValid = true; - Resources res = mContext.getResources(); - mDividerSize = DockedDividerUtils.getDividerSize(res, - DockedDividerUtils.getDividerInsets(res)); - mDividerSizeInactive = (int) TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, DIVIDER_WIDTH_INACTIVE_DP, res.getDisplayMetrics()); - } - - int getPrimarySplitSide() { - switch (mDisplayLayout.getNavigationBarPosition(mContext.getResources())) { - case DisplayLayout.NAV_BAR_BOTTOM: - return mDisplayLayout.isLandscape() ? DOCKED_LEFT : DOCKED_TOP; - case DisplayLayout.NAV_BAR_LEFT: - return DOCKED_RIGHT; - case DisplayLayout.NAV_BAR_RIGHT: - return DOCKED_LEFT; - default: - return DOCKED_INVALID; - } - } - - DividerSnapAlgorithm getSnapAlgorithm() { - if (mSnapAlgorithm == null) { - updateResources(); - boolean isHorizontalDivision = !mDisplayLayout.isLandscape(); - mSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(), - mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize, - isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide()); - } - return mSnapAlgorithm; - } - - DividerSnapAlgorithm getMinimizedSnapAlgorithm(boolean homeStackResizable) { - if (mMinimizedSnapAlgorithm == null) { - updateResources(); - boolean isHorizontalDivision = !mDisplayLayout.isLandscape(); - mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(), - mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize, - isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide(), - true /* isMinimized */, homeStackResizable); - } - return mMinimizedSnapAlgorithm; - } - - /** - * Resize primary bounds and secondary bounds by divider position. - * - * @param position divider position. - * @return true if calculated bounds changed. - */ - boolean resizeSplits(int position) { - mPrimary = mPrimary == null ? new Rect() : mPrimary; - mSecondary = mSecondary == null ? new Rect() : mSecondary; - int dockSide = getPrimarySplitSide(); - boolean boundsChanged; - - mTmpBounds.set(mPrimary); - DockedDividerUtils.calculateBoundsForPosition(position, dockSide, mPrimary, - mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize); - boundsChanged = !mPrimary.equals(mTmpBounds); - - mTmpBounds.set(mSecondary); - DockedDividerUtils.calculateBoundsForPosition(position, - DockedDividerUtils.invertDockSide(dockSide), mSecondary, mDisplayLayout.width(), - mDisplayLayout.height(), mDividerSize); - boundsChanged |= !mSecondary.equals(mTmpBounds); - return boundsChanged; - } - - void resizeSplits(int position, WindowContainerTransaction t) { - if (resizeSplits(position)) { - t.setBounds(mTiles.mPrimary.token, mPrimary); - t.setBounds(mTiles.mSecondary.token, mSecondary); - - t.setSmallestScreenWidthDp(mTiles.mPrimary.token, - getSmallestWidthDpForBounds(mContext, mDisplayLayout, mPrimary)); - t.setSmallestScreenWidthDp(mTiles.mSecondary.token, - getSmallestWidthDpForBounds(mContext, mDisplayLayout, mSecondary)); - } - } - - Rect calcResizableMinimizedHomeStackBounds() { - DividerSnapAlgorithm.SnapTarget miniMid = - getMinimizedSnapAlgorithm(true /* resizable */).getMiddleTarget(); - Rect homeBounds = new Rect(); - DockedDividerUtils.calculateBoundsForPosition(miniMid.position, - DockedDividerUtils.invertDockSide(getPrimarySplitSide()), homeBounds, - mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize); - return homeBounds; - } - - /** - * Updates the adjustment depending on it's current state. - */ - void updateAdjustedBounds(int currImeTop, int hiddenTop, int shownTop) { - adjustForIME(mDisplayLayout, currImeTop, hiddenTop, shownTop, mDividerSize, - mDividerSizeInactive, mPrimary, mSecondary); - } - - /** Assumes top/bottom split. Splits are not adjusted for left/right splits. */ - private void adjustForIME(DisplayLayout dl, int currImeTop, int hiddenTop, int shownTop, - int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) { - if (mAdjustedPrimary == null) { - mAdjustedPrimary = new Rect(); - mAdjustedSecondary = new Rect(); - } - - final Rect displayStableRect = new Rect(); - dl.getStableBounds(displayStableRect); - - final float shownFraction = ((float) (currImeTop - hiddenTop)) / (shownTop - hiddenTop); - final int currDividerWidth = - (int) (dividerWidthInactive * shownFraction + dividerWidth * (1.f - shownFraction)); - - // Calculate the highest we can move the bottom of the top stack to keep 30% visible. - final int minTopStackBottom = displayStableRect.top - + (int) ((mPrimary.bottom - displayStableRect.top) * ADJUSTED_STACK_FRACTION_MIN); - // Based on that, calculate the maximum amount we'll allow the ime to shift things. - final int maxOffset = mPrimary.bottom - minTopStackBottom; - // Calculate how much we would shift things without limits (basically the height of ime). - final int desiredOffset = hiddenTop - shownTop; - // Calculate an "adjustedTop" which is the currImeTop but restricted by our constraints. - // We want an effect where the adjustment only occurs during the "highest" portion of the - // ime animation. This is done by shifting the adjustment values by the difference in - // offsets (effectively playing the whole adjustment animation some fixed amount of pixels - // below the ime top). - final int topCorrection = Math.max(0, desiredOffset - maxOffset); - final int adjustedTop = currImeTop + topCorrection; - // The actual yOffset is the distance between adjustedTop and the bottom of the display. - // Since our adjustedTop values are playing "below" the ime, we clamp at 0 so we only - // see adjustment upward. - final int yOffset = Math.max(0, dl.height() - adjustedTop); - - // TOP - // Reduce the offset by an additional small amount to squish the divider bar. - mAdjustedPrimary.set(primaryBounds); - mAdjustedPrimary.offset(0, -yOffset + (dividerWidth - currDividerWidth)); - - // BOTTOM - mAdjustedSecondary.set(secondaryBounds); - mAdjustedSecondary.offset(0, -yOffset); - } - - static int getSmallestWidthDpForBounds(@NonNull Context context, DisplayLayout dl, - Rect bounds) { - int dividerSize = DockedDividerUtils.getDividerSize(context.getResources(), - DockedDividerUtils.getDividerInsets(context.getResources())); - - int minWidth = Integer.MAX_VALUE; - - // Go through all screen orientations and find the orientation in which the task has the - // smallest width. - Rect tmpRect = new Rect(); - Rect rotatedDisplayRect = new Rect(); - Rect displayRect = new Rect(0, 0, dl.width(), dl.height()); - - DisplayLayout tmpDL = new DisplayLayout(); - for (int rotation = 0; rotation < 4; rotation++) { - tmpDL.set(dl); - tmpDL.rotateTo(context.getResources(), rotation); - DividerSnapAlgorithm snap = initSnapAlgorithmForRotation(context, tmpDL, dividerSize); - - tmpRect.set(bounds); - rotateBounds(tmpRect, displayRect, dl.rotation(), rotation); - rotatedDisplayRect.set(0, 0, tmpDL.width(), tmpDL.height()); - final int dockSide = getPrimarySplitSide(tmpRect, rotatedDisplayRect, - tmpDL.getOrientation()); - final int position = DockedDividerUtils.calculatePositionForBounds(tmpRect, dockSide, - dividerSize); - - final int snappedPosition = - snap.calculateNonDismissingSnapTarget(position).position; - DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, tmpRect, - tmpDL.width(), tmpDL.height(), dividerSize); - Rect insettedDisplay = new Rect(rotatedDisplayRect); - insettedDisplay.inset(tmpDL.stableInsets()); - tmpRect.intersect(insettedDisplay); - minWidth = Math.min(tmpRect.width(), minWidth); - } - return (int) (minWidth / dl.density()); - } - - static DividerSnapAlgorithm initSnapAlgorithmForRotation(Context context, DisplayLayout dl, - int dividerSize) { - final Configuration config = new Configuration(); - config.unset(); - config.orientation = dl.getOrientation(); - Rect tmpRect = new Rect(0, 0, dl.width(), dl.height()); - tmpRect.inset(dl.nonDecorInsets()); - config.windowConfiguration.setAppBounds(tmpRect); - tmpRect.set(0, 0, dl.width(), dl.height()); - tmpRect.inset(dl.stableInsets()); - config.screenWidthDp = (int) (tmpRect.width() / dl.density()); - config.screenHeightDp = (int) (tmpRect.height() / dl.density()); - final Context rotationContext = context.createConfigurationContext(config); - return new DividerSnapAlgorithm( - rotationContext.getResources(), dl.width(), dl.height(), dividerSize, - config.orientation == ORIENTATION_PORTRAIT, dl.stableInsets()); - } - - /** - * Get the current primary-split side. Determined by its location of {@param bounds} within - * {@param displayRect} but if both are the same, it will try to dock to each side and determine - * if allowed in its respected {@param orientation}. - * - * @param bounds bounds of the primary split task to get which side is docked - * @param displayRect bounds of the display that contains the primary split task - * @param orientation the origination of device - * @return current primary-split side - */ - static int getPrimarySplitSide(Rect bounds, Rect displayRect, int orientation) { - if (orientation == ORIENTATION_PORTRAIT) { - // Portrait mode, docked either at the top or the bottom. - final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top); - if (diff < 0) { - return DOCKED_BOTTOM; - } else { - // Top is default - return DOCKED_TOP; - } - } else if (orientation == ORIENTATION_LANDSCAPE) { - // Landscape mode, docked either on the left or on the right. - final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left); - if (diff < 0) { - return DOCKED_RIGHT; - } - return DOCKED_LEFT; - } - return DOCKED_INVALID; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreen.java deleted file mode 100644 index 499a9c5fa631..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreen.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.legacysplitscreen; - -import android.graphics.Rect; -import android.window.WindowContainerToken; - -import com.android.wm.shell.common.annotations.ExternalThread; - -import java.io.PrintWriter; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -/** - * Interface to engage split screen feature. - */ -@ExternalThread -public interface LegacySplitScreen { - /** Called when keyguard showing state changed. */ - void onKeyguardVisibilityChanged(boolean isShowing); - - /** Returns {@link DividerView}. */ - DividerView getDividerView(); - - /** Returns {@code true} if one of the split screen is in minimized mode. */ - boolean isMinimized(); - - /** Returns {@code true} if the home stack is resizable. */ - boolean isHomeStackResizable(); - - /** Returns {@code true} if the divider is visible. */ - boolean isDividerVisible(); - - /** Switch to minimized state if appropriate. */ - void setMinimized(boolean minimized); - - /** Called when there's a task undocking. */ - void onUndockingTask(); - - /** Called when app transition finished. */ - void onAppTransitionFinished(); - - /** Dumps current status of Split Screen. */ - void dump(PrintWriter pw); - - /** Registers listener that gets called whenever the existence of the divider changes. */ - void registerInSplitScreenListener(Consumer<Boolean> listener); - - /** Unregisters listener that gets called whenever the existence of the divider changes. */ - void unregisterInSplitScreenListener(Consumer<Boolean> listener); - - /** Registers listener that gets called whenever the split screen bounds changes. */ - void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener); - - /** @return the container token for the secondary split root task. */ - WindowContainerToken getSecondaryRoot(); - - /** - * Splits the primary task if feasible, this is to preserve legacy way to toggle split screen. - * Like triggering split screen through long pressing recents app button or through - * {@link android.accessibilityservice.AccessibilityService#GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN}. - * - * @return {@code true} if it successes to split the primary task. - */ - boolean splitPrimaryTask(); - - /** - * Exits the split to make the primary task fullscreen. - */ - void dismissSplitToPrimaryTask(); -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java deleted file mode 100644 index 67e487de0993..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java +++ /dev/null @@ -1,762 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.legacysplitscreen; - -import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.view.Display.DEFAULT_DISPLAY; - -import android.animation.AnimationHandler; -import android.app.ActivityManager; -import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityTaskManager; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.Rect; -import android.os.RemoteException; -import android.provider.Settings; -import android.util.Slog; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.Toast; -import android.window.TaskOrganizer; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.internal.policy.DividerSnapAlgorithm; -import com.android.wm.shell.R; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayChangeController; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.SystemWindows; -import com.android.wm.shell.common.TaskStackListenerCallback; -import com.android.wm.shell.common.TaskStackListenerImpl; -import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.transition.Transitions; - -import java.io.PrintWriter; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -/** - * Controls split screen feature. - */ -public class LegacySplitScreenController implements DisplayController.OnDisplaysChangedListener { - static final boolean DEBUG = false; - - private static final String TAG = "SplitScreenCtrl"; - private static final int DEFAULT_APP_TRANSITION_DURATION = 336; - - private final Context mContext; - private final DisplayChangeController.OnDisplayChangingListener mRotationController; - private final DisplayController mDisplayController; - private final DisplayImeController mImeController; - private final DividerImeController mImePositionProcessor; - private final DividerState mDividerState = new DividerState(); - private final ForcedResizableInfoActivityController mForcedResizableController; - private final ShellExecutor mMainExecutor; - private final AnimationHandler mSfVsyncAnimationHandler; - private final LegacySplitScreenTaskListener mSplits; - private final SystemWindows mSystemWindows; - final TransactionPool mTransactionPool; - private final WindowManagerProxy mWindowManagerProxy; - private final TaskOrganizer mTaskOrganizer; - private final SplitScreenImpl mImpl = new SplitScreenImpl(); - - private final CopyOnWriteArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners - = new CopyOnWriteArrayList<>(); - private final ArrayList<WeakReference<BiConsumer<Rect, Rect>>> mBoundsChangedListeners = - new ArrayList<>(); - - - private DividerWindowManager mWindowManager; - private DividerView mView; - - // Keeps track of real-time split geometry including snap positions and ime adjustments - private LegacySplitDisplayLayout mSplitLayout; - - // Transient: this contains the layout calculated for a new rotation requested by WM. This is - // kept around so that we can wait for a matching configuration change and then use the exact - // layout that we sent back to WM. - private LegacySplitDisplayLayout mRotateSplitLayout; - - private boolean mIsKeyguardShowing; - private boolean mVisible = false; - private volatile boolean mMinimized = false; - private volatile boolean mAdjustedForIme = false; - private boolean mHomeStackResizable = false; - - public LegacySplitScreenController(Context context, - DisplayController displayController, SystemWindows systemWindows, - DisplayImeController imeController, TransactionPool transactionPool, - ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, - TaskStackListenerImpl taskStackListener, Transitions transitions, - ShellExecutor mainExecutor, AnimationHandler sfVsyncAnimationHandler) { - mContext = context; - mDisplayController = displayController; - mSystemWindows = systemWindows; - mImeController = imeController; - mMainExecutor = mainExecutor; - mSfVsyncAnimationHandler = sfVsyncAnimationHandler; - mForcedResizableController = new ForcedResizableInfoActivityController(context, this, - mainExecutor); - mTransactionPool = transactionPool; - mWindowManagerProxy = new WindowManagerProxy(syncQueue, shellTaskOrganizer); - mTaskOrganizer = shellTaskOrganizer; - mSplits = new LegacySplitScreenTaskListener(this, shellTaskOrganizer, transitions, - syncQueue); - mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mMainExecutor, - shellTaskOrganizer); - mRotationController = - (display, fromRotation, toRotation, wct) -> { - if (!mSplits.isSplitScreenSupported() || mWindowManagerProxy == null) { - return; - } - WindowContainerTransaction t = new WindowContainerTransaction(); - DisplayLayout displayLayout = - new DisplayLayout(mDisplayController.getDisplayLayout(display)); - LegacySplitDisplayLayout sdl = - new LegacySplitDisplayLayout(mContext, displayLayout, mSplits); - sdl.rotateTo(toRotation); - mRotateSplitLayout = sdl; - // snap resets to middle target when not minimized and rotation changed. - final int position = mMinimized ? mView.mSnapTargetBeforeMinimized.position - : sdl.getSnapAlgorithm().getMiddleTarget().position; - DividerSnapAlgorithm snap = sdl.getSnapAlgorithm(); - final DividerSnapAlgorithm.SnapTarget target = - snap.calculateNonDismissingSnapTarget(position); - sdl.resizeSplits(target.position, t); - - if (isSplitActive() && mHomeStackResizable) { - mWindowManagerProxy - .applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t); - } - if (mWindowManagerProxy.queueSyncTransactionIfWaiting(t)) { - // Because sync transactions are serialized, its possible for an "older" - // bounds-change to get applied after a screen rotation. In that case, we - // want to actually defer on that rather than apply immediately. Of course, - // this means that the bounds may not change until after the rotation so - // the user might see some artifacts. This should be rare. - Slog.w(TAG, "Screen rotated while other operations were pending, this may" - + " result in some graphical artifacts."); - } else { - wct.merge(t, true /* transfer */); - } - }; - - mWindowManager = new DividerWindowManager(mSystemWindows); - - // No need to listen to display window container or create root tasks if the device is not - // using legacy split screen. - if (!context.getResources().getBoolean(com.android.internal.R.bool.config_useLegacySplit)) { - return; - } - - - mDisplayController.addDisplayWindowListener(this); - // Don't initialize the divider or anything until we get the default display. - - taskStackListener.addListener( - new TaskStackListenerCallback() { - @Override - public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task, - boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) { - if (!wasVisible || task.getWindowingMode() - != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY - || !mSplits.isSplitScreenSupported()) { - return; - } - - if (isMinimized()) { - onUndockingTask(); - } - } - - @Override - public void onActivityForcedResizable(String packageName, int taskId, - int reason) { - mForcedResizableController.activityForcedResizable(packageName, taskId, - reason); - } - - @Override - public void onActivityDismissingDockedStack() { - mForcedResizableController.activityDismissingSplitScreen(); - } - - @Override - public void onActivityLaunchOnSecondaryDisplayFailed() { - mForcedResizableController.activityLaunchOnSecondaryDisplayFailed(); - } - }); - } - - public LegacySplitScreen asLegacySplitScreen() { - return mImpl; - } - - public void onSplitScreenSupported() { - // Set starting tile bounds based on middle target - final WindowContainerTransaction tct = new WindowContainerTransaction(); - int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; - mSplitLayout.resizeSplits(midPos, tct); - mTaskOrganizer.applyTransaction(tct); - } - - public void onKeyguardVisibilityChanged(boolean showing) { - if (!isSplitActive() || mView == null) { - return; - } - mView.setHidden(showing); - mIsKeyguardShowing = showing; - } - - @Override - public void onDisplayAdded(int displayId) { - if (displayId != DEFAULT_DISPLAY) { - return; - } - mSplitLayout = new LegacySplitDisplayLayout(mDisplayController.getDisplayContext(displayId), - mDisplayController.getDisplayLayout(displayId), mSplits); - mImeController.addPositionProcessor(mImePositionProcessor); - mDisplayController.addDisplayChangingController(mRotationController); - if (!ActivityTaskManager.supportsSplitScreenMultiWindow(mContext)) { - removeDivider(); - return; - } - try { - mSplits.init(); - } catch (Exception e) { - Slog.e(TAG, "Failed to register docked stack listener", e); - removeDivider(); - return; - } - } - - @Override - public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { - if (displayId != DEFAULT_DISPLAY || !mSplits.isSplitScreenSupported()) { - return; - } - mSplitLayout = new LegacySplitDisplayLayout(mDisplayController.getDisplayContext(displayId), - mDisplayController.getDisplayLayout(displayId), mSplits); - if (mRotateSplitLayout == null) { - int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position; - final WindowContainerTransaction tct = new WindowContainerTransaction(); - mSplitLayout.resizeSplits(midPos, tct); - mTaskOrganizer.applyTransaction(tct); - } else if (mSplitLayout.mDisplayLayout.rotation() - == mRotateSplitLayout.mDisplayLayout.rotation()) { - mSplitLayout.mPrimary = new Rect(mRotateSplitLayout.mPrimary); - mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary); - mRotateSplitLayout = null; - } - if (isSplitActive()) { - update(newConfig); - } - } - - public boolean isMinimized() { - return mMinimized; - } - - public boolean isHomeStackResizable() { - return mHomeStackResizable; - } - - public DividerView getDividerView() { - return mView; - } - - public boolean isDividerVisible() { - return mView != null && mView.getVisibility() == View.VISIBLE; - } - - /** - * This indicates that at-least one of the splits has content. This differs from - * isDividerVisible because the divider is only visible once *everything* is in split mode - * while this only cares if some things are (eg. while entering/exiting as well). - */ - public boolean isSplitActive() { - return mSplits.mPrimary != null && mSplits.mSecondary != null - && (mSplits.mPrimary.topActivityType != ACTIVITY_TYPE_UNDEFINED - || mSplits.mSecondary.topActivityType != ACTIVITY_TYPE_UNDEFINED); - } - - public void addDivider(Configuration configuration) { - Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId()); - mView = (DividerView) - LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null); - mView.setAnimationHandler(mSfVsyncAnimationHandler); - DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId()); - mView.injectDependencies(this, mWindowManager, mDividerState, mForcedResizableController, - mSplits, mSplitLayout, mImePositionProcessor, mWindowManagerProxy); - mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE); - mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */); - final int size = dctx.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.docked_stack_divider_thickness); - final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE; - final int width = landscape ? size : displayLayout.width(); - final int height = landscape ? displayLayout.height() : size; - mWindowManager.add(mView, width, height, mContext.getDisplayId()); - } - - public void removeDivider() { - if (mView != null) { - mView.onDividerRemoved(); - } - mWindowManager.remove(); - } - - public void update(Configuration configuration) { - final boolean isDividerHidden = mView != null && mIsKeyguardShowing; - - removeDivider(); - addDivider(configuration); - - if (mMinimized) { - mView.setMinimizedDockStack(true, mHomeStackResizable, null /* transaction */); - updateTouchable(); - } - mView.setHidden(isDividerHidden); - } - - public void onTaskVanished() { - removeDivider(); - } - - public void updateVisibility(final boolean visible) { - if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible); - if (mVisible != visible) { - mVisible = visible; - mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - - if (visible) { - mView.enterSplitMode(mHomeStackResizable); - // Update state because animations won't finish. - mWindowManagerProxy.runInSync( - t -> mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, t)); - - } else { - mView.exitSplitMode(); - mWindowManagerProxy.runInSync( - t -> mView.setMinimizedDockStack(false, mHomeStackResizable, t)); - } - // Notify existence listeners - synchronized (mDockedStackExistsListeners) { - mDockedStackExistsListeners.removeIf(wf -> { - Consumer<Boolean> l = wf.get(); - if (l != null) l.accept(visible); - return l == null; - }); - } - } - } - - public void setMinimized(final boolean minimized) { - if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible); - mMainExecutor.execute(() -> { - if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible); - if (!mVisible) { - return; - } - setHomeMinimized(minimized); - }); - } - - public void setHomeMinimized(final boolean minimized) { - if (DEBUG) { - Slog.d(TAG, "setHomeMinimized min:" + mMinimized + "->" + minimized + " hrsz:" - + mHomeStackResizable + " split:" + isDividerVisible()); - } - WindowContainerTransaction wct = new WindowContainerTransaction(); - final boolean minimizedChanged = mMinimized != minimized; - // Update minimized state - if (minimizedChanged) { - mMinimized = minimized; - } - // Always set this because we could be entering split when mMinimized is already true - wct.setFocusable(mSplits.mPrimary.token, !mMinimized); - - // Sync state to DividerView if it exists. - if (mView != null) { - final int displayId = mView.getDisplay() != null - ? mView.getDisplay().getDisplayId() : DEFAULT_DISPLAY; - // pause ime here (before updateMinimizedDockedStack) - if (mMinimized) { - mImePositionProcessor.pause(displayId); - } - if (minimizedChanged) { - // This conflicts with IME adjustment, so only call it when things change. - mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable); - } - if (!mMinimized) { - // afterwards so it can end any animations started in view - mImePositionProcessor.resume(displayId); - } - } - updateTouchable(); - - // If we are only setting focusability, a sync transaction isn't necessary (in fact it - // can interrupt other animations), so see if it can be submitted on pending instead. - if (!mWindowManagerProxy.queueSyncTransactionIfWaiting(wct)) { - mTaskOrganizer.applyTransaction(wct); - } - } - - public void setAdjustedForIme(boolean adjustedForIme) { - if (mAdjustedForIme == adjustedForIme) { - return; - } - mAdjustedForIme = adjustedForIme; - updateTouchable(); - } - - public void updateTouchable() { - mWindowManager.setTouchable(!mAdjustedForIme); - } - - public void onUndockingTask() { - if (mView != null) { - mView.onUndockingTask(); - } - } - - public void onAppTransitionFinished() { - if (mView == null) { - return; - } - mForcedResizableController.onAppTransitionFinished(); - } - - public void dump(PrintWriter pw) { - pw.print(" mVisible="); pw.println(mVisible); - pw.print(" mMinimized="); pw.println(mMinimized); - pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme); - } - - public long getAnimDuration() { - float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(), - Settings.Global.TRANSITION_ANIMATION_SCALE, - mContext.getResources().getFloat( - com.android.internal.R.dimen - .config_appTransitionAnimationDurationScaleDefault)); - final long transitionDuration = DEFAULT_APP_TRANSITION_DURATION; - return (long) (transitionDuration * transitionScale); - } - - public void registerInSplitScreenListener(Consumer<Boolean> listener) { - listener.accept(isDividerVisible()); - synchronized (mDockedStackExistsListeners) { - mDockedStackExistsListeners.add(new WeakReference<>(listener)); - } - } - - public void unregisterInSplitScreenListener(Consumer<Boolean> listener) { - synchronized (mDockedStackExistsListeners) { - for (int i = mDockedStackExistsListeners.size() - 1; i >= 0; i--) { - if (mDockedStackExistsListeners.get(i) == listener) { - mDockedStackExistsListeners.remove(i); - } - } - } - } - - public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) { - synchronized (mBoundsChangedListeners) { - mBoundsChangedListeners.add(new WeakReference<>(listener)); - } - } - - public boolean splitPrimaryTask() { - try { - if (ActivityTaskManager.getService().getLockTaskModeState() == LOCK_TASK_MODE_PINNED) { - return false; - } - } catch (RemoteException e) { - return false; - } - if (isSplitActive() || mSplits.mPrimary == null) { - return false; - } - - // Try fetching the top running task. - final List<RunningTaskInfo> runningTasks = - ActivityTaskManager.getInstance().getTasks(1 /* maxNum */); - if (runningTasks == null || runningTasks.isEmpty()) { - return false; - } - // Note: The set of running tasks from the system is ordered by recency. - final RunningTaskInfo topRunningTask = runningTasks.get(0); - final int activityType = topRunningTask.getActivityType(); - if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { - return false; - } - - if (!topRunningTask.supportsSplitScreenMultiWindow) { - Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text, - Toast.LENGTH_SHORT).show(); - return false; - } - - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Clear out current windowing mode before reparenting to split task. - wct.setWindowingMode(topRunningTask.token, WINDOWING_MODE_UNDEFINED); - wct.reparent(topRunningTask.token, mSplits.mPrimary.token, true /* onTop */); - mWindowManagerProxy.applySyncTransaction(wct); - return true; - } - - public void dismissSplitToPrimaryTask() { - startDismissSplit(true /* toPrimaryTask */); - } - - /** Notifies the bounds of split screen changed. */ - public void notifyBoundsChanged(Rect secondaryWindowBounds, Rect secondaryWindowInsets) { - synchronized (mBoundsChangedListeners) { - mBoundsChangedListeners.removeIf(wf -> { - BiConsumer<Rect, Rect> l = wf.get(); - if (l != null) l.accept(secondaryWindowBounds, secondaryWindowInsets); - return l == null; - }); - } - } - - public void startEnterSplit() { - update(mDisplayController.getDisplayContext( - mContext.getDisplayId()).getResources().getConfiguration()); - // Set resizable directly here because applyEnterSplit already resizes home stack. - mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits, - mRotateSplitLayout != null ? mRotateSplitLayout : mSplitLayout); - } - - public void prepareEnterSplitTransition(WindowContainerTransaction outWct) { - // Set resizable directly here because buildEnterSplit already resizes home stack. - mHomeStackResizable = mWindowManagerProxy.buildEnterSplit(outWct, mSplits, - mRotateSplitLayout != null ? mRotateSplitLayout : mSplitLayout); - } - - public void finishEnterSplitTransition(boolean minimized) { - update(mDisplayController.getDisplayContext( - mContext.getDisplayId()).getResources().getConfiguration()); - if (minimized) { - ensureMinimizedSplit(); - } else { - ensureNormalSplit(); - } - } - - public void startDismissSplit(boolean toPrimaryTask) { - startDismissSplit(toPrimaryTask, false /* snapped */); - } - - public void startDismissSplit(boolean toPrimaryTask, boolean snapped) { - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - mSplits.getSplitTransitions().dismissSplit( - mSplits, mSplitLayout, !toPrimaryTask, snapped); - } else { - mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, !toPrimaryTask); - onDismissSplit(); - } - } - - public void onDismissSplit() { - updateVisibility(false /* visible */); - mMinimized = false; - // Resets divider bar position to undefined, so new divider bar will apply default position - // next time entering split mode. - mDividerState.mRatioPositionBeforeMinimized = 0; - removeDivider(); - mImePositionProcessor.reset(); - } - - public void ensureMinimizedSplit() { - setHomeMinimized(true /* minimized */); - if (mView != null && !isDividerVisible()) { - // Wasn't in split-mode yet, so enter now. - if (DEBUG) { - Slog.d(TAG, " entering split mode with minimized=true"); - } - updateVisibility(true /* visible */); - } - } - - public void ensureNormalSplit() { - setHomeMinimized(false /* minimized */); - if (mView != null && !isDividerVisible()) { - // Wasn't in split-mode, so enter now. - if (DEBUG) { - Slog.d(TAG, " enter split mode unminimized "); - } - updateVisibility(true /* visible */); - } - } - - public LegacySplitDisplayLayout getSplitLayout() { - return mSplitLayout; - } - - public WindowManagerProxy getWmProxy() { - return mWindowManagerProxy; - } - - public WindowContainerToken getSecondaryRoot() { - if (mSplits == null || mSplits.mSecondary == null) { - return null; - } - return mSplits.mSecondary.token; - } - - private class SplitScreenImpl implements LegacySplitScreen { - @Override - public boolean isMinimized() { - return mMinimized; - } - - @Override - public boolean isHomeStackResizable() { - return mHomeStackResizable; - } - - /** - * TODO: Remove usage from outside the shell. - */ - @Override - public DividerView getDividerView() { - return LegacySplitScreenController.this.getDividerView(); - } - - @Override - public boolean isDividerVisible() { - boolean[] result = new boolean[1]; - try { - mMainExecutor.executeBlocking(() -> { - result[0] = LegacySplitScreenController.this.isDividerVisible(); - }); - } catch (InterruptedException e) { - Slog.e(TAG, "Failed to get divider visible"); - } - return result[0]; - } - - @Override - public void onKeyguardVisibilityChanged(boolean isShowing) { - mMainExecutor.execute(() -> { - LegacySplitScreenController.this.onKeyguardVisibilityChanged(isShowing); - }); - } - - @Override - public void setMinimized(boolean minimized) { - mMainExecutor.execute(() -> { - LegacySplitScreenController.this.setMinimized(minimized); - }); - } - - @Override - public void onUndockingTask() { - mMainExecutor.execute(() -> { - LegacySplitScreenController.this.onUndockingTask(); - }); - } - - @Override - public void onAppTransitionFinished() { - mMainExecutor.execute(() -> { - LegacySplitScreenController.this.onAppTransitionFinished(); - }); - } - - @Override - public void registerInSplitScreenListener(Consumer<Boolean> listener) { - mMainExecutor.execute(() -> { - LegacySplitScreenController.this.registerInSplitScreenListener(listener); - }); - } - - @Override - public void unregisterInSplitScreenListener(Consumer<Boolean> listener) { - mMainExecutor.execute(() -> { - LegacySplitScreenController.this.unregisterInSplitScreenListener(listener); - }); - } - - @Override - public void registerBoundsChangeListener(BiConsumer<Rect, Rect> listener) { - mMainExecutor.execute(() -> { - LegacySplitScreenController.this.registerBoundsChangeListener(listener); - }); - } - - @Override - public WindowContainerToken getSecondaryRoot() { - WindowContainerToken[] result = new WindowContainerToken[1]; - try { - mMainExecutor.executeBlocking(() -> { - result[0] = LegacySplitScreenController.this.getSecondaryRoot(); - }); - } catch (InterruptedException e) { - Slog.e(TAG, "Failed to get secondary root"); - } - return result[0]; - } - - @Override - public boolean splitPrimaryTask() { - boolean[] result = new boolean[1]; - try { - mMainExecutor.executeBlocking(() -> { - result[0] = LegacySplitScreenController.this.splitPrimaryTask(); - }); - } catch (InterruptedException e) { - Slog.e(TAG, "Failed to split primary task"); - } - return result[0]; - } - - @Override - public void dismissSplitToPrimaryTask() { - mMainExecutor.execute(() -> { - LegacySplitScreenController.this.dismissSplitToPrimaryTask(); - }); - } - - @Override - public void dump(PrintWriter pw) { - try { - mMainExecutor.executeBlocking(() -> { - LegacySplitScreenController.this.dump(pw); - }); - } catch (InterruptedException e) { - Slog.e(TAG, "Failed to dump LegacySplitScreenController in 2s"); - } - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java deleted file mode 100644 index d2f42c39acd5..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTaskListener.java +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.legacysplitscreen; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; -import static android.view.Display.DEFAULT_DISPLAY; - -import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; - -import android.app.ActivityManager.RunningTaskInfo; -import android.graphics.Point; -import android.graphics.Rect; -import android.util.Log; -import android.util.SparseArray; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.window.TaskOrganizer; - -import androidx.annotation.NonNull; - -import com.android.internal.protolog.common.ProtoLog; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.SurfaceUtils; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.transition.Transitions; - -import java.io.PrintWriter; -import java.util.ArrayList; - -class LegacySplitScreenTaskListener implements ShellTaskOrganizer.TaskListener { - private static final String TAG = LegacySplitScreenTaskListener.class.getSimpleName(); - private static final boolean DEBUG = LegacySplitScreenController.DEBUG; - - private final ShellTaskOrganizer mTaskOrganizer; - private final SyncTransactionQueue mSyncQueue; - private final SparseArray<SurfaceControl> mLeashByTaskId = new SparseArray<>(); - - // TODO(shell-transitions): Remove when switched to shell-transitions. - private final SparseArray<Point> mPositionByTaskId = new SparseArray<>(); - - RunningTaskInfo mPrimary; - RunningTaskInfo mSecondary; - SurfaceControl mPrimarySurface; - SurfaceControl mSecondarySurface; - SurfaceControl mPrimaryDim; - SurfaceControl mSecondaryDim; - Rect mHomeBounds = new Rect(); - final LegacySplitScreenController mSplitScreenController; - private boolean mSplitScreenSupported = false; - - final SurfaceSession mSurfaceSession = new SurfaceSession(); - - private final LegacySplitScreenTransitions mSplitTransitions; - - LegacySplitScreenTaskListener(LegacySplitScreenController splitScreenController, - ShellTaskOrganizer shellTaskOrganizer, - Transitions transitions, - SyncTransactionQueue syncQueue) { - mSplitScreenController = splitScreenController; - mTaskOrganizer = shellTaskOrganizer; - mSplitTransitions = new LegacySplitScreenTransitions(splitScreenController.mTransactionPool, - transitions, mSplitScreenController, this); - transitions.addHandler(mSplitTransitions); - mSyncQueue = syncQueue; - } - - void init() { - synchronized (this) { - try { - mTaskOrganizer.createRootTask( - DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, this); - mTaskOrganizer.createRootTask( - DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, this); - } catch (Exception e) { - // teardown to prevent callbacks - mTaskOrganizer.removeListener(this); - throw e; - } - } - } - - boolean isSplitScreenSupported() { - return mSplitScreenSupported; - } - - SurfaceControl.Transaction getTransaction() { - return mSplitScreenController.mTransactionPool.acquire(); - } - - void releaseTransaction(SurfaceControl.Transaction t) { - mSplitScreenController.mTransactionPool.release(t); - } - - TaskOrganizer getTaskOrganizer() { - return mTaskOrganizer; - } - - LegacySplitScreenTransitions getSplitTransitions() { - return mSplitTransitions; - } - - @Override - public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { - synchronized (this) { - if (taskInfo.hasParentTask()) { - handleChildTaskAppeared(taskInfo, leash); - return; - } - - final int winMode = taskInfo.getWindowingMode(); - if (winMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - ProtoLog.v(WM_SHELL_TASK_ORG, - "%s onTaskAppeared Primary taskId=%d", TAG, taskInfo.taskId); - mPrimary = taskInfo; - mPrimarySurface = leash; - } else if (winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { - ProtoLog.v(WM_SHELL_TASK_ORG, - "%s onTaskAppeared Secondary taskId=%d", TAG, taskInfo.taskId); - mSecondary = taskInfo; - mSecondarySurface = leash; - } else { - ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared unknown taskId=%d winMode=%d", - TAG, taskInfo.taskId, winMode); - } - - if (!mSplitScreenSupported && mPrimarySurface != null && mSecondarySurface != null) { - mSplitScreenSupported = true; - mSplitScreenController.onSplitScreenSupported(); - ProtoLog.v(WM_SHELL_TASK_ORG, "%s onTaskAppeared Supported", TAG); - - // Initialize dim surfaces: - SurfaceControl.Transaction t = getTransaction(); - mPrimaryDim = SurfaceUtils.makeDimLayer( - t, mPrimarySurface, "Primary Divider Dim", mSurfaceSession); - mSecondaryDim = SurfaceUtils.makeDimLayer( - t, mSecondarySurface, "Secondary Divider Dim", mSurfaceSession); - t.apply(); - releaseTransaction(t); - } - } - } - - @Override - public void onTaskVanished(RunningTaskInfo taskInfo) { - synchronized (this) { - mPositionByTaskId.remove(taskInfo.taskId); - if (taskInfo.hasParentTask()) { - mLeashByTaskId.remove(taskInfo.taskId); - return; - } - - final boolean isPrimaryTask = mPrimary != null - && taskInfo.token.equals(mPrimary.token); - final boolean isSecondaryTask = mSecondary != null - && taskInfo.token.equals(mSecondary.token); - - if (mSplitScreenSupported && (isPrimaryTask || isSecondaryTask)) { - mSplitScreenSupported = false; - - SurfaceControl.Transaction t = getTransaction(); - t.remove(mPrimaryDim); - t.remove(mSecondaryDim); - t.remove(mPrimarySurface); - t.remove(mSecondarySurface); - t.apply(); - releaseTransaction(t); - - mSplitScreenController.onTaskVanished(); - } - } - } - - @Override - public void onTaskInfoChanged(RunningTaskInfo taskInfo) { - if (taskInfo.displayId != DEFAULT_DISPLAY) { - return; - } - synchronized (this) { - if (!taskInfo.supportsMultiWindow) { - if (mSplitScreenController.isDividerVisible()) { - // Dismiss the split screen if the task no longer supports multi window. - if (taskInfo.taskId == mPrimary.taskId - || taskInfo.parentTaskId == mPrimary.taskId) { - // If the primary is focused, dismiss to primary. - mSplitScreenController - .startDismissSplit(taskInfo.isFocused /* toPrimaryTask */); - } else { - // If the secondary is not focused, dismiss to primary. - mSplitScreenController - .startDismissSplit(!taskInfo.isFocused /* toPrimaryTask */); - } - } - return; - } - if (taskInfo.hasParentTask()) { - // changed messages are noisy since it reports on every ensureVisibility. This - // conflicts with legacy app-transitions which "swaps" the position to a - // leash. For now, only update when position actually changes to avoid - // poorly-timed duplicate calls. - if (taskInfo.positionInParent.equals(mPositionByTaskId.get(taskInfo.taskId))) { - return; - } - handleChildTaskChanged(taskInfo); - } else { - handleTaskInfoChanged(taskInfo); - } - mPositionByTaskId.put(taskInfo.taskId, new Point(taskInfo.positionInParent)); - } - } - - private void handleChildTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { - mLeashByTaskId.put(taskInfo.taskId, leash); - mPositionByTaskId.put(taskInfo.taskId, new Point(taskInfo.positionInParent)); - if (Transitions.ENABLE_SHELL_TRANSITIONS) return; - updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */); - } - - private void handleChildTaskChanged(RunningTaskInfo taskInfo) { - if (Transitions.ENABLE_SHELL_TRANSITIONS) return; - final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId); - updateChildTaskSurface(taskInfo, leash, false /* firstAppeared */); - } - - private void updateChildTaskSurface( - RunningTaskInfo taskInfo, SurfaceControl leash, boolean firstAppeared) { - final Point taskPositionInParent = taskInfo.positionInParent; - mSyncQueue.runInSync(t -> { - t.setWindowCrop(leash, null); - t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y); - if (firstAppeared && !Transitions.ENABLE_SHELL_TRANSITIONS) { - t.setAlpha(leash, 1f); - t.setMatrix(leash, 1, 0, 0, 1); - t.show(leash); - } - }); - } - - /** - * This is effectively a finite state machine which moves between the various split-screen - * presentations based on the contents of the split regions. - */ - private void handleTaskInfoChanged(RunningTaskInfo info) { - if (!mSplitScreenSupported) { - // This shouldn't happen; but apparently there is a chance that SysUI crashes without - // system server receiving binder-death (or maybe it receives binder-death too late?). - // In this situation, when sys-ui restarts, the split root-tasks will still exist so - // there is a small window of time during init() where WM might send messages here - // before init() fails. So, avoid a cycle of crashes by returning early. - Log.e(TAG, "Got handleTaskInfoChanged when not initialized: " + info); - return; - } - final boolean secondaryImpliedMinimize = mSecondary.topActivityType == ACTIVITY_TYPE_HOME - || (mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS - && mSplitScreenController.isHomeStackResizable()); - final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED; - final boolean secondaryWasEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED; - if (info.token.asBinder() == mPrimary.token.asBinder()) { - mPrimary = info; - } else if (info.token.asBinder() == mSecondary.token.asBinder()) { - mSecondary = info; - } - if (DEBUG) { - Log.d(TAG, "onTaskInfoChanged " + mPrimary + " " + mSecondary); - } - if (Transitions.ENABLE_SHELL_TRANSITIONS) return; - final boolean primaryIsEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED; - final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED; - final boolean secondaryImpliesMinimize = mSecondary.topActivityType == ACTIVITY_TYPE_HOME - || (mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS - && mSplitScreenController.isHomeStackResizable()); - if (primaryIsEmpty == primaryWasEmpty && secondaryWasEmpty == secondaryIsEmpty - && secondaryImpliedMinimize == secondaryImpliesMinimize) { - // No relevant changes - return; - } - if (primaryIsEmpty || secondaryIsEmpty) { - // At-least one of the splits is empty which means we are currently transitioning - // into or out-of split-screen mode. - if (DEBUG) { - Log.d(TAG, " at-least one split empty " + mPrimary.topActivityType - + " " + mSecondary.topActivityType); - } - if (mSplitScreenController.isDividerVisible()) { - // Was in split-mode, which means we are leaving split, so continue that. - // This happens when the stack in the primary-split is dismissed. - if (DEBUG) { - Log.d(TAG, " was in split, so this means leave it " - + mPrimary.topActivityType + " " + mSecondary.topActivityType); - } - mSplitScreenController.startDismissSplit(false /* toPrimaryTask */); - } else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) { - // Wasn't in split-mode (both were empty), but now that the primary split is - // populated, we should fully enter split by moving everything else into secondary. - // This just tells window-manager to reparent things, the UI will respond - // when it gets new task info for the secondary split. - if (DEBUG) { - Log.d(TAG, " was not in split, but primary is populated, so enter it"); - } - mSplitScreenController.startEnterSplit(); - } - } else if (secondaryImpliesMinimize) { - // Workaround for b/172686383, we can't rely on the sync bounds change transaction for - // the home task to finish before the last updateChildTaskSurface() call even if it's - // queued on the sync transaction queue, so ensure that the home task surface is updated - // again before we minimize - final ArrayList<RunningTaskInfo> tasks = new ArrayList<>(); - mSplitScreenController.getWmProxy().getHomeAndRecentsTasks(tasks, - mSplitScreenController.getSecondaryRoot()); - for (int i = 0; i < tasks.size(); i++) { - final RunningTaskInfo taskInfo = tasks.get(i); - final SurfaceControl leash = mLeashByTaskId.get(taskInfo.taskId); - if (leash != null) { - updateChildTaskSurface(taskInfo, leash, false /* firstAppeared */); - } - } - - // Both splits are populated but the secondary split has a home/recents stack on top, - // so enter minimized mode. - mSplitScreenController.ensureMinimizedSplit(); - } else { - // Both splits are populated by normal activities, so make sure we aren't minimized. - mSplitScreenController.ensureNormalSplit(); - } - } - - @Override - public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { - b.setParent(findTaskSurface(taskId)); - } - - @Override - public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, - SurfaceControl.Transaction t) { - t.reparent(sc, findTaskSurface(taskId)); - } - - private SurfaceControl findTaskSurface(int taskId) { - if (!mLeashByTaskId.contains(taskId)) { - throw new IllegalArgumentException("There is no surface for taskId=" + taskId); - } - return mLeashByTaskId.get(taskId); - } - - @Override - public void dump(@NonNull PrintWriter pw, String prefix) { - final String innerPrefix = prefix + " "; - final String childPrefix = innerPrefix + " "; - pw.println(prefix + this); - pw.println(innerPrefix + "mSplitScreenSupported=" + mSplitScreenSupported); - if (mPrimary != null) pw.println(innerPrefix + "mPrimary.taskId=" + mPrimary.taskId); - if (mSecondary != null) pw.println(innerPrefix + "mSecondary.taskId=" + mSecondary.taskId); - } - - @Override - public String toString() { - return TAG; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java deleted file mode 100644 index b1fa2ac25fe7..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenTransitions.java +++ /dev/null @@ -1,348 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.legacysplitscreen; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_CLOSE; -import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM; -import static android.view.WindowManager.TRANSIT_OPEN; -import static android.view.WindowManager.TRANSIT_TO_BACK; -import static android.view.WindowManager.TRANSIT_TO_FRONT; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.WindowConfiguration; -import android.graphics.Rect; -import android.os.IBinder; -import android.view.SurfaceControl; -import android.view.WindowManager; -import android.window.TransitionInfo; -import android.window.TransitionRequestInfo; -import android.window.WindowContainerTransaction; - -import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.common.annotations.ExternalThread; -import com.android.wm.shell.transition.Transitions; - -import java.util.ArrayList; - -/** Plays transition animations for split-screen */ -public class LegacySplitScreenTransitions implements Transitions.TransitionHandler { - private static final String TAG = "SplitScreenTransitions"; - - public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 10; - - private final TransactionPool mTransactionPool; - private final Transitions mTransitions; - private final LegacySplitScreenController mSplitScreen; - private final LegacySplitScreenTaskListener mListener; - - private IBinder mPendingDismiss = null; - private boolean mDismissFromSnap = false; - private IBinder mPendingEnter = null; - private IBinder mAnimatingTransition = null; - - /** Keeps track of currently running animations */ - private final ArrayList<Animator> mAnimations = new ArrayList<>(); - - private Transitions.TransitionFinishCallback mFinishCallback = null; - private SurfaceControl.Transaction mFinishTransaction; - - LegacySplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions, - @NonNull LegacySplitScreenController splitScreen, - @NonNull LegacySplitScreenTaskListener listener) { - mTransactionPool = pool; - mTransitions = transitions; - mSplitScreen = splitScreen; - mListener = listener; - } - - @Override - public WindowContainerTransaction handleRequest(@NonNull IBinder transition, - @Nullable TransitionRequestInfo request) { - WindowContainerTransaction out = null; - final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); - final @WindowManager.TransitionType int type = request.getType(); - if (mSplitScreen.isDividerVisible()) { - // try to handle everything while in split-screen - out = new WindowContainerTransaction(); - if (triggerTask != null) { - final boolean shouldDismiss = - // if we close the primary-docked task, then leave split-screen since there - // is nothing behind it. - ((type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK) - && triggerTask.parentTaskId == mListener.mPrimary.taskId) - // if an activity that is not supported in multi window mode is launched, - // we also need to leave split-screen. - || ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT) - && !triggerTask.supportsMultiWindow); - // In both cases, dismiss the primary - if (shouldDismiss) { - WindowManagerProxy.buildDismissSplit(out, mListener, - mSplitScreen.getSplitLayout(), true /* dismiss */); - if (type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT) { - out.reorder(triggerTask.token, true /* onTop */); - } - mPendingDismiss = transition; - } - } - } else if (triggerTask != null) { - // Not in split mode, so look for an open with a trigger task. - if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT) - && triggerTask.configuration.windowConfiguration.getWindowingMode() - == WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - out = new WindowContainerTransaction(); - mSplitScreen.prepareEnterSplitTransition(out); - mPendingEnter = transition; - } - } - return out; - } - - // TODO(shell-transitions): real animations - private void startExampleAnimation(@NonNull SurfaceControl leash, boolean show) { - final float end = show ? 1.f : 0.f; - final float start = 1.f - end; - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final ValueAnimator va = ValueAnimator.ofFloat(start, end); - va.setDuration(500); - va.addUpdateListener(animation -> { - float fraction = animation.getAnimatedFraction(); - transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); - transaction.apply(); - }); - final Runnable finisher = () -> { - transaction.setAlpha(leash, end); - transaction.apply(); - mTransactionPool.release(transaction); - mTransitions.getMainExecutor().execute(() -> { - mAnimations.remove(va); - onFinish(); - }); - }; - va.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { } - - @Override - public void onAnimationEnd(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationCancel(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationRepeat(Animator animation) { } - }); - mAnimations.add(va); - mTransitions.getAnimExecutor().execute(va::start); - } - - // TODO(shell-transitions): real animations - private void startExampleResizeAnimation(@NonNull SurfaceControl leash, - @NonNull Rect startBounds, @NonNull Rect endBounds) { - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final ValueAnimator va = ValueAnimator.ofFloat(0.f, 1.f); - va.setDuration(500); - va.addUpdateListener(animation -> { - float fraction = animation.getAnimatedFraction(); - transaction.setWindowCrop(leash, - (int) (startBounds.width() * (1.f - fraction) + endBounds.width() * fraction), - (int) (startBounds.height() * (1.f - fraction) - + endBounds.height() * fraction)); - transaction.setPosition(leash, - startBounds.left * (1.f - fraction) + endBounds.left * fraction, - startBounds.top * (1.f - fraction) + endBounds.top * fraction); - transaction.apply(); - }); - final Runnable finisher = () -> { - transaction.setWindowCrop(leash, 0, 0); - transaction.setPosition(leash, endBounds.left, endBounds.top); - transaction.apply(); - mTransactionPool.release(transaction); - mTransitions.getMainExecutor().execute(() -> { - mAnimations.remove(va); - onFinish(); - }); - }; - va.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationCancel(Animator animation) { - finisher.run(); - } - }); - mAnimations.add(va); - mTransitions.getAnimExecutor().execute(va::start); - } - - @Override - public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (transition != mPendingDismiss && transition != mPendingEnter) { - // If we're not in split-mode, just abort - if (!mSplitScreen.isDividerVisible()) return false; - // Check to see if HOME is involved - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getTaskInfo() == null - || change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME) continue; - if (change.getMode() == TRANSIT_OPEN || change.getMode() == TRANSIT_TO_FRONT) { - mSplitScreen.ensureMinimizedSplit(); - } else if (change.getMode() == TRANSIT_CLOSE - || change.getMode() == TRANSIT_TO_BACK) { - mSplitScreen.ensureNormalSplit(); - } - } - // Use normal animations. - return false; - } - - mFinishCallback = finishCallback; - mFinishTransaction = mTransactionPool.acquire(); - mAnimatingTransition = transition; - - // Play fade animations - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final SurfaceControl leash = change.getLeash(); - final int mode = info.getChanges().get(i).getMode(); - - if (mode == TRANSIT_CHANGE) { - if (change.getParent() != null) { - // This is probably reparented, so we want the parent to be immediately visible - final TransitionInfo.Change parentChange = info.getChange(change.getParent()); - startTransaction.show(parentChange.getLeash()); - startTransaction.setAlpha(parentChange.getLeash(), 1.f); - // and then animate this layer outside the parent (since, for example, this is - // the home task animating from fullscreen to part-screen). - startTransaction.reparent(leash, info.getRootLeash()); - startTransaction.setLayer(leash, info.getChanges().size() - i); - // build the finish reparent/reposition - mFinishTransaction.reparent(leash, parentChange.getLeash()); - mFinishTransaction.setPosition(leash, - change.getEndRelOffset().x, change.getEndRelOffset().y); - } - // TODO(shell-transitions): screenshot here - final Rect startBounds = new Rect(change.getStartAbsBounds()); - final boolean isHome = change.getTaskInfo() != null - && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME; - if (mPendingDismiss == transition && mDismissFromSnap && !isHome) { - // Home is special since it doesn't move during fling. Everything else, though, - // when dismissing from snap, the top/left is at 0,0. - startBounds.offsetTo(0, 0); - } - final Rect endBounds = new Rect(change.getEndAbsBounds()); - startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y); - endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y); - startExampleResizeAnimation(leash, startBounds, endBounds); - } - if (change.getParent() != null) { - continue; - } - - if (transition == mPendingEnter - && mListener.mPrimary.token.equals(change.getContainer()) - || mListener.mSecondary.token.equals(change.getContainer())) { - startTransaction.setWindowCrop(leash, change.getStartAbsBounds().width(), - change.getStartAbsBounds().height()); - if (mListener.mPrimary.token.equals(change.getContainer())) { - // Move layer to top since we want it above the oversized home task during - // animation even though home task is on top in hierarchy. - startTransaction.setLayer(leash, info.getChanges().size() + 1); - } - } - boolean isOpening = Transitions.isOpeningType(info.getType()); - if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { - // fade in - startExampleAnimation(leash, true /* show */); - } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { - // fade out - if (transition == mPendingDismiss && mDismissFromSnap) { - // Dismissing via snap-to-top/bottom means that the dismissed task is already - // not-visible (usually cropped to oblivion) so immediately set its alpha to 0 - // and don't animate it so it doesn't pop-in when reparented. - startTransaction.setAlpha(leash, 0.f); - } else { - startExampleAnimation(leash, false /* show */); - } - } - } - if (transition == mPendingEnter) { - // If entering, check if we should enter into minimized or normal split - boolean homeIsVisible = false; - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getTaskInfo() == null - || change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME) { - continue; - } - homeIsVisible = change.getMode() == TRANSIT_OPEN - || change.getMode() == TRANSIT_TO_FRONT - || change.getMode() == TRANSIT_CHANGE; - break; - } - mSplitScreen.finishEnterSplitTransition(homeIsVisible); - } - startTransaction.apply(); - onFinish(); - return true; - } - - @ExternalThread - void dismissSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout, - boolean dismissOrMaximize, boolean snapped) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - WindowManagerProxy.buildDismissSplit(wct, tiles, layout, dismissOrMaximize); - mTransitions.getMainExecutor().execute(() -> { - mDismissFromSnap = snapped; - mPendingDismiss = mTransitions.startTransition(TRANSIT_SPLIT_DISMISS_SNAP, wct, this); - }); - } - - private void onFinish() { - if (!mAnimations.isEmpty()) return; - mFinishTransaction.apply(); - mTransactionPool.release(mFinishTransaction); - mFinishTransaction = null; - mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); - mFinishCallback = null; - if (mAnimatingTransition == mPendingEnter) { - mPendingEnter = null; - } - if (mAnimatingTransition == mPendingDismiss) { - mSplitScreen.onDismissSplit(); - mPendingDismiss = null; - } - mDismissFromSnap = false; - mAnimatingTransition = null; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/MinimizedDockShadow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/MinimizedDockShadow.java deleted file mode 100644 index 1e9223cbe3e2..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/MinimizedDockShadow.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2016 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.wm.shell.legacysplitscreen; - -import android.annotation.Nullable; -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.LinearGradient; -import android.graphics.Paint; -import android.graphics.Shader; -import android.util.AttributeSet; -import android.view.View; -import android.view.WindowManager; - -import com.android.wm.shell.R; - -/** - * Shadow for the minimized dock state on homescreen. - */ -public class MinimizedDockShadow extends View { - - private final Paint mShadowPaint = new Paint(); - - private int mDockSide = WindowManager.DOCKED_INVALID; - - public MinimizedDockShadow(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - } - - void setDockSide(int dockSide) { - if (dockSide != mDockSide) { - mDockSide = dockSide; - updatePaint(getLeft(), getTop(), getRight(), getBottom()); - invalidate(); - } - } - - private void updatePaint(int left, int top, int right, int bottom) { - int startColor = mContext.getResources().getColor( - R.color.minimize_dock_shadow_start, null); - int endColor = mContext.getResources().getColor( - R.color.minimize_dock_shadow_end, null); - final int middleColor = Color.argb( - (Color.alpha(startColor) + Color.alpha(endColor)) / 2, 0, 0, 0); - final int quarter = Color.argb( - (int) (Color.alpha(startColor) * 0.25f + Color.alpha(endColor) * 0.75f), - 0, 0, 0); - if (mDockSide == WindowManager.DOCKED_TOP) { - mShadowPaint.setShader(new LinearGradient( - 0, 0, 0, bottom - top, - new int[] { startColor, middleColor, quarter, endColor }, - new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP)); - } else if (mDockSide == WindowManager.DOCKED_LEFT) { - mShadowPaint.setShader(new LinearGradient( - 0, 0, right - left, 0, - new int[] { startColor, middleColor, quarter, endColor }, - new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP)); - } else if (mDockSide == WindowManager.DOCKED_RIGHT) { - mShadowPaint.setShader(new LinearGradient( - right - left, 0, 0, 0, - new int[] { startColor, middleColor, quarter, endColor }, - new float[] { 0f, 0.35f, 0.6f, 1f }, Shader.TileMode.CLAMP)); - } - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - super.onLayout(changed, left, top, right, bottom); - if (changed) { - updatePaint(left, top, right, bottom); - invalidate(); - } - } - - @Override - protected void onDraw(Canvas canvas) { - canvas.drawRect(0, 0, getWidth(), getHeight(), mShadowPaint); - } - - @Override - public boolean hasOverlappingRendering() { - return false; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java deleted file mode 100644 index e42e43bbc2be..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/WindowManagerProxy.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * 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.wm.shell.legacysplitscreen; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.content.res.Configuration.ORIENTATION_UNDEFINED; -import static android.view.Display.DEFAULT_DISPLAY; - -import android.annotation.NonNull; -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.graphics.Rect; -import android.os.RemoteException; -import android.util.Log; -import android.view.Display; -import android.view.SurfaceControl; -import android.view.WindowManagerGlobal; -import android.window.TaskOrganizer; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.internal.annotations.GuardedBy; -import com.android.internal.util.ArrayUtils; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.transition.Transitions; - -import java.util.ArrayList; -import java.util.List; - -/** - * Proxy to simplify calls into window manager/activity manager - */ -class WindowManagerProxy { - - private static final String TAG = "WindowManagerProxy"; - private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS}; - private static final int[] CONTROLLED_ACTIVITY_TYPES = { - ACTIVITY_TYPE_STANDARD, - ACTIVITY_TYPE_HOME, - ACTIVITY_TYPE_RECENTS, - ACTIVITY_TYPE_UNDEFINED - }; - private static final int[] CONTROLLED_WINDOWING_MODES = { - WINDOWING_MODE_FULLSCREEN, - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, - WINDOWING_MODE_UNDEFINED - }; - - @GuardedBy("mDockedRect") - private final Rect mDockedRect = new Rect(); - - private final Rect mTmpRect1 = new Rect(); - - @GuardedBy("mDockedRect") - private final Rect mTouchableRegion = new Rect(); - - private final SyncTransactionQueue mSyncTransactionQueue; - private final TaskOrganizer mTaskOrganizer; - - WindowManagerProxy(SyncTransactionQueue syncQueue, TaskOrganizer taskOrganizer) { - mSyncTransactionQueue = syncQueue; - mTaskOrganizer = taskOrganizer; - } - - void dismissOrMaximizeDocked(final LegacySplitScreenTaskListener tiles, - LegacySplitDisplayLayout layout, final boolean dismissOrMaximize) { - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - tiles.mSplitScreenController.startDismissSplit(!dismissOrMaximize, true /* snapped */); - } else { - applyDismissSplit(tiles, layout, dismissOrMaximize); - } - } - - public void setResizing(final boolean resizing) { - try { - ActivityTaskManager.getService().setSplitScreenResizing(resizing); - } catch (RemoteException e) { - Log.w(TAG, "Error calling setDockedStackResizing: " + e); - } - } - - /** Sets a touch region */ - public void setTouchRegion(Rect region) { - try { - synchronized (mDockedRect) { - mTouchableRegion.set(region); - } - WindowManagerGlobal.getWindowManagerService().setDockedTaskDividerTouchRegion( - mTouchableRegion); - } catch (RemoteException e) { - Log.w(TAG, "Failed to set touchable region: " + e); - } - } - - void applyResizeSplits(int position, LegacySplitDisplayLayout splitLayout) { - WindowContainerTransaction t = new WindowContainerTransaction(); - splitLayout.resizeSplits(position, t); - applySyncTransaction(t); - } - - boolean getHomeAndRecentsTasks(List<ActivityManager.RunningTaskInfo> out, - WindowContainerToken parent) { - boolean resizable = false; - List<ActivityManager.RunningTaskInfo> rootTasks = parent == null - ? mTaskOrganizer.getRootTasks(Display.DEFAULT_DISPLAY, HOME_AND_RECENTS) - : mTaskOrganizer.getChildTasks(parent, HOME_AND_RECENTS); - for (int i = 0, n = rootTasks.size(); i < n; ++i) { - final ActivityManager.RunningTaskInfo ti = rootTasks.get(i); - out.add(ti); - if (ti.topActivityType == ACTIVITY_TYPE_HOME) { - resizable = ti.isResizeable; - } - } - return resizable; - } - - /** - * Assign a fixed override-bounds to home tasks that reflect their geometry while the primary - * split is minimized. This actually "sticks out" of the secondary split area, but when in - * minimized mode, the secondary split gets a 'negative' crop to expose it. - */ - boolean applyHomeTasksMinimized(LegacySplitDisplayLayout layout, WindowContainerToken parent, - @NonNull WindowContainerTransaction wct) { - // Resize the home/recents stacks to the larger minimized-state size - final Rect homeBounds; - final ArrayList<ActivityManager.RunningTaskInfo> homeStacks = new ArrayList<>(); - boolean isHomeResizable = getHomeAndRecentsTasks(homeStacks, parent); - if (isHomeResizable) { - homeBounds = layout.calcResizableMinimizedHomeStackBounds(); - } else { - // home is not resizable, so lock it to its inherent orientation size. - homeBounds = new Rect(0, 0, 0, 0); - for (int i = homeStacks.size() - 1; i >= 0; --i) { - if (homeStacks.get(i).topActivityType == ACTIVITY_TYPE_HOME) { - final int orient = homeStacks.get(i).configuration.orientation; - final boolean displayLandscape = layout.mDisplayLayout.isLandscape(); - final boolean isLandscape = orient == ORIENTATION_LANDSCAPE - || (orient == ORIENTATION_UNDEFINED && displayLandscape); - homeBounds.right = isLandscape == displayLandscape - ? layout.mDisplayLayout.width() : layout.mDisplayLayout.height(); - homeBounds.bottom = isLandscape == displayLandscape - ? layout.mDisplayLayout.height() : layout.mDisplayLayout.width(); - break; - } - } - } - for (int i = homeStacks.size() - 1; i >= 0; --i) { - // For non-resizable homes, the minimized size is actually the fullscreen-size. As a - // result, we don't minimize for recents since it only shows half-size screenshots. - if (!isHomeResizable) { - if (homeStacks.get(i).topActivityType == ACTIVITY_TYPE_RECENTS) { - continue; - } - wct.setWindowingMode(homeStacks.get(i).token, WINDOWING_MODE_FULLSCREEN); - } - wct.setBounds(homeStacks.get(i).token, homeBounds); - } - layout.mTiles.mHomeBounds.set(homeBounds); - return isHomeResizable; - } - - /** @see #buildEnterSplit */ - boolean applyEnterSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout) { - // Set launchtile first so that any stack created after - // getAllRootTaskInfos and before reparent (even if unlikely) are placed - // correctly. - WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.setLaunchRoot(tiles.mSecondary.token, CONTROLLED_WINDOWING_MODES, - CONTROLLED_ACTIVITY_TYPES); - final boolean isHomeResizable = buildEnterSplit(wct, tiles, layout); - applySyncTransaction(wct); - return isHomeResizable; - } - - /** - * Finishes entering split-screen by reparenting all FULLSCREEN tasks into the secondary split. - * This assumes there is already something in the primary split since that is usually what - * triggers a call to this. In the same transaction, this overrides the home task bounds via - * {@link #applyHomeTasksMinimized}. - * - * @return whether the home stack is resizable - */ - boolean buildEnterSplit(WindowContainerTransaction outWct, LegacySplitScreenTaskListener tiles, - LegacySplitDisplayLayout layout) { - List<ActivityManager.RunningTaskInfo> rootTasks = - mTaskOrganizer.getRootTasks(DEFAULT_DISPLAY, null /* activityTypes */); - if (rootTasks.isEmpty()) { - return false; - } - ActivityManager.RunningTaskInfo topHomeTask = null; - for (int i = rootTasks.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo rootTask = rootTasks.get(i); - // Check whether the task can be moved to split secondary. - if (!rootTask.supportsMultiWindow && rootTask.topActivityType != ACTIVITY_TYPE_HOME) { - continue; - } - // Only move split controlling tasks to split secondary. - final int windowingMode = rootTask.getWindowingMode(); - if (!ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, windowingMode) - || !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, rootTask.getActivityType()) - // Excludes split screen secondary due to it's the root we're reparenting to. - || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) { - continue; - } - // Since this iterates from bottom to top, update topHomeTask for every fullscreen task - // so it will be left with the status of the top one. - topHomeTask = isHomeOrRecentTask(rootTask) ? rootTask : null; - outWct.reparent(rootTask.token, tiles.mSecondary.token, true /* onTop */); - } - // Move the secondary split-forward. - outWct.reorder(tiles.mSecondary.token, true /* onTop */); - boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, - outWct); - if (topHomeTask != null && !Transitions.ENABLE_SHELL_TRANSITIONS) { - // Translate/update-crop of secondary out-of-band with sync transaction -- Until BALST - // is enabled, this temporarily syncs the home surface position with offset until - // sync transaction finishes. - outWct.setBoundsChangeTransaction(topHomeTask.token, tiles.mHomeBounds); - } - return isHomeResizable; - } - - static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) { - final int atype = ti.getActivityType(); - return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS; - } - - /** @see #buildDismissSplit */ - void applyDismissSplit(LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout, - boolean dismissOrMaximize) { - // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished - // plus specific APIs to clean this up. - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Set launch root first so that any task created after getChildContainers and - // before reparent (pretty unlikely) are put into fullscreen. - wct.setLaunchRoot(tiles.mSecondary.token, null, null); - buildDismissSplit(wct, tiles, layout, dismissOrMaximize); - applySyncTransaction(wct); - } - - /** - * Reparents all tile members back to their display and resets home task override bounds. - * @param dismissOrMaximize When {@code true} this resolves the split by closing the primary - * split (thus resulting in the top of the secondary split becoming - * fullscreen. {@code false} resolves the other way. - */ - static void buildDismissSplit(WindowContainerTransaction outWct, - LegacySplitScreenTaskListener tiles, LegacySplitDisplayLayout layout, - boolean dismissOrMaximize) { - // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished - // plus specific APIs to clean this up. - final TaskOrganizer taskOrg = tiles.getTaskOrganizer(); - List<ActivityManager.RunningTaskInfo> primaryChildren = - taskOrg.getChildTasks(tiles.mPrimary.token, null /* activityTypes */); - List<ActivityManager.RunningTaskInfo> secondaryChildren = - taskOrg.getChildTasks(tiles.mSecondary.token, null /* activityTypes */); - // In some cases (eg. non-resizable is launched), system-server will leave split-screen. - // as a result, the above will not capture any tasks; yet, we need to clean-up the - // home task bounds. - List<ActivityManager.RunningTaskInfo> freeHomeAndRecents = - taskOrg.getRootTasks(DEFAULT_DISPLAY, HOME_AND_RECENTS); - // Filter out the root split tasks - freeHomeAndRecents.removeIf(p -> p.token.equals(tiles.mSecondary.token) - || p.token.equals(tiles.mPrimary.token)); - - if (primaryChildren.isEmpty() && secondaryChildren.isEmpty() - && freeHomeAndRecents.isEmpty()) { - return; - } - if (dismissOrMaximize) { - // Dismissing, so move all primary split tasks first - for (int i = primaryChildren.size() - 1; i >= 0; --i) { - outWct.reparent(primaryChildren.get(i).token, null /* parent */, - true /* onTop */); - } - boolean homeOnTop = false; - // Don't need to worry about home tasks because they are already in the "proper" - // order within the secondary split. - for (int i = secondaryChildren.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i); - outWct.reparent(ti.token, null /* parent */, true /* onTop */); - if (isHomeOrRecentTask(ti)) { - outWct.setBounds(ti.token, null); - outWct.setWindowingMode(ti.token, WINDOWING_MODE_UNDEFINED); - if (i == 0) { - homeOnTop = true; - } - } - } - if (homeOnTop && !Transitions.ENABLE_SHELL_TRANSITIONS) { - // Translate/update-crop of secondary out-of-band with sync transaction -- instead - // play this in sync with new home-app frame because until BALST is enabled this - // shows up on screen before the syncTransaction returns. - // We only have access to the secondary root surface, though, so in order to - // position things properly, we have to take into account the existing negative - // offset/crop of the minimized-home task. - final boolean landscape = layout.mDisplayLayout.isLandscape(); - final int posX = landscape ? layout.mSecondary.left - tiles.mHomeBounds.left - : layout.mSecondary.left; - final int posY = landscape ? layout.mSecondary.top - : layout.mSecondary.top - tiles.mHomeBounds.top; - final SurfaceControl.Transaction sft = new SurfaceControl.Transaction(); - sft.setPosition(tiles.mSecondarySurface, posX, posY); - final Rect crop = new Rect(0, 0, layout.mDisplayLayout.width(), - layout.mDisplayLayout.height()); - crop.offset(-posX, -posY); - sft.setWindowCrop(tiles.mSecondarySurface, crop); - outWct.setBoundsChangeTransaction(tiles.mSecondary.token, sft); - } - } else { - // Maximize, so move non-home secondary split first - for (int i = secondaryChildren.size() - 1; i >= 0; --i) { - if (isHomeOrRecentTask(secondaryChildren.get(i))) { - continue; - } - outWct.reparent(secondaryChildren.get(i).token, null /* parent */, - true /* onTop */); - } - // Find and place home tasks in-between. This simulates the fact that there was - // nothing behind the primary split's tasks. - for (int i = secondaryChildren.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i); - if (isHomeOrRecentTask(ti)) { - outWct.reparent(ti.token, null /* parent */, true /* onTop */); - // reset bounds and mode too - outWct.setBounds(ti.token, null); - outWct.setWindowingMode(ti.token, WINDOWING_MODE_UNDEFINED); - } - } - for (int i = primaryChildren.size() - 1; i >= 0; --i) { - outWct.reparent(primaryChildren.get(i).token, null /* parent */, - true /* onTop */); - } - } - for (int i = freeHomeAndRecents.size() - 1; i >= 0; --i) { - outWct.setBounds(freeHomeAndRecents.get(i).token, null); - outWct.setWindowingMode(freeHomeAndRecents.get(i).token, WINDOWING_MODE_UNDEFINED); - } - // Reset focusable to true - outWct.setFocusable(tiles.mPrimary.token, true /* focusable */); - } - - /** - * Utility to apply a sync transaction serially with other sync transactions. - * - * @see SyncTransactionQueue#queue - */ - void applySyncTransaction(WindowContainerTransaction wct) { - mSyncTransactionQueue.queue(wct); - } - - /** - * @see SyncTransactionQueue#queueIfWaiting - */ - boolean queueSyncTransactionIfWaiting(WindowContainerTransaction wct) { - return mSyncTransactionQueue.queueIfWaiting(wct); - } - - /** - * @see SyncTransactionQueue#runInSync - */ - void runInSync(SyncTransactionQueue.TransactionRunnable runnable) { - mSyncTransactionQueue.runInSync(runnable); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index 179b725ab210..1d8ac2b576e9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -39,6 +39,7 @@ import android.provider.Settings; import android.util.Slog; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import android.window.DisplayAreaInfo; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; @@ -659,11 +660,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, } /** - * Handles rotation based on OnDisplayChangingListener callback + * Handles display change based on OnDisplayChangingListener callback */ @Override - public void onRotateDisplay(int displayId, int fromRotation, int toRotation, - WindowContainerTransaction wct) { + public void onDisplayChange(int displayId, int fromRotation, int toRotation, + DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) { if (!isInitialized()) { return; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index e624de661737..99e7dc5065bf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -297,6 +297,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, displayController.addDisplayWindowListener(this); } + public PipTransitionController getTransitionController() { + return mPipTransitionController; + } + public Rect getCurrentOrAnimatingBounds() { PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getCurrentAnimator(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 36e712459863..28427a808d90 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -28,7 +28,6 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; -import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; @@ -52,6 +51,7 @@ import android.graphics.Rect; import android.os.IBinder; import android.view.Surface; import android.view.SurfaceControl; +import android.view.WindowManager; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerToken; @@ -145,6 +145,11 @@ public class PipTransition extends PipTransitionController { if (destinationBounds != null) { mExitDestinationBounds.set(destinationBounds); } + final PipAnimationController.PipTransitionAnimator animator = + mPipAnimationController.getCurrentAnimator(); + if (animator != null && animator.isRunning()) { + animator.cancel(); + } mExitTransition = mTransitions.startTransition(type, out, this); } @@ -217,13 +222,20 @@ public class PipTransition extends PipTransitionController { } // Entering PIP. - if (isEnteringPip(info, mCurrentPipTaskToken)) { - return startEnterAnimation(info, startTransaction, finishTransaction, finishCallback); + if (isEnteringPip(info)) { + startEnterAnimation(info, startTransaction, finishTransaction, finishCallback); + return true; } // For transition that we don't animate, but contains the PIP leash, we need to update the // PIP surface, otherwise it will be reset after the transition. if (currentPipTaskChange != null) { + // Set the "end" bounds of pip. The default setup uses the start bounds. Since this is + // changing the *finish*Transaction, we need to use the end bounds. This will also + // make sure that the fade-in animation (below) uses the end bounds as well. + if (!currentPipTaskChange.getEndAbsBounds().isEmpty()) { + mPipBoundsState.setBounds(currentPipTaskChange.getEndAbsBounds()); + } updatePipForUnhandledTransition(currentPipTaskChange, startTransaction, finishTransaction); } @@ -245,16 +257,9 @@ public class PipTransition extends PipTransitionController { @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { - if (request.getType() == TRANSIT_PIP) { + if (requestHasPipEnter(request)) { WindowContainerTransaction wct = new WindowContainerTransaction(); - if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { - mRequestedEnterTransition = transition; - mRequestedEnterTask = request.getTriggerTask().token; - wct.setActivityWindowingMode(request.getTriggerTask().token, - WINDOWING_MODE_UNDEFINED); - final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); - wct.setBounds(request.getTriggerTask().token, destinationBounds); - } + augmentRequest(transition, request, wct); return wct; } else { return null; @@ -262,6 +267,22 @@ public class PipTransition extends PipTransitionController { } @Override + public void augmentRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) { + if (!requestHasPipEnter(request)) { + throw new IllegalStateException("Called PiP augmentRequest when request has no PiP"); + } + if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + mRequestedEnterTransition = transition; + mRequestedEnterTask = request.getTriggerTask().token; + outWCT.setActivityWindowingMode(request.getTriggerTask().token, + WINDOWING_MODE_UNDEFINED); + final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + outWCT.setBounds(request.getTriggerTask().token, destinationBounds); + } + } + + @Override public boolean handleRotateDisplay(int startRotation, int endRotation, WindowContainerTransaction wct) { if (mRequestedEnterTransition != null && mOneShotAnimationType == ANIM_TYPE_ALPHA) { @@ -315,11 +336,27 @@ public class PipTransition extends PipTransitionController { // (likely a remote like launcher), so don't fire the finish-callback here -- wait until // the exit transition is merged. if ((mExitTransition == null || isAnimatingLocally()) && mFinishCallback != null) { - WindowContainerTransaction wct = new WindowContainerTransaction(); - prepareFinishResizeTransaction(taskInfo, destinationBounds, - direction, wct); - if (tx != null) { - wct.setBoundsChangeTransaction(taskInfo.token, tx); + WindowContainerTransaction wct = null; + if (isOutPipDirection(direction)) { + // Only need to reset surface properties. The server-side operations were already + // done at the start. + if (tx != null) { + mFinishTransaction.merge(tx); + } + } else { + wct = new WindowContainerTransaction(); + if (isInPipDirection(direction)) { + // If we are animating from fullscreen using a bounds animation, then reset the + // activity windowing mode, and set the task bounds to the final bounds + wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); + wct.scheduleFinishEnterPip(taskInfo.token, destinationBounds); + wct.setBounds(taskInfo.token, destinationBounds); + } else { + wct.setBounds(taskInfo.token, null /* bounds */); + } + if (tx != null) { + wct.setBoundsChangeTransaction(taskInfo.token, tx); + } } final SurfaceControl leash = mPipOrganizer.getSurfaceControl(); final int displayRotation = taskInfo.getConfiguration().windowConfiguration @@ -559,92 +596,94 @@ public class PipTransition extends PipTransitionController { } /** Whether we should handle the given {@link TransitionInfo} animation as entering PIP. */ - private static boolean isEnteringPip(@NonNull TransitionInfo info, - @Nullable WindowContainerToken currentPipTaskToken) { + private boolean isEnteringPip(@NonNull TransitionInfo info) { for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); - if (change.getTaskInfo() != null - && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED - && !change.getContainer().equals(currentPipTaskToken)) { - // We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps - // that enter PiP instantly on opening, mostly from CTS/Flicker tests) - if (info.getType() == TRANSIT_PIP || info.getType() == TRANSIT_OPEN) { - return true; - } - // This can happen if the request to enter PIP happens when we are collecting for - // another transition, such as TRANSIT_CHANGE (display rotation). - if (info.getType() == TRANSIT_CHANGE) { - return true; - } + if (isEnteringPip(change, info.getType())) return true; + } + return false; + } - // Please file a bug to handle the unexpected transition type. - throw new IllegalStateException("Entering PIP with unexpected transition type=" - + transitTypeToString(info.getType())); + /** Whether a particular change is a window that is entering pip. */ + @Override + public boolean isEnteringPip(@NonNull TransitionInfo.Change change, + @WindowManager.TransitionType int transitType) { + if (change.getTaskInfo() != null + && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED + && !change.getContainer().equals(mCurrentPipTaskToken)) { + // We support TRANSIT_PIP type (from RootWindowContainer) or TRANSIT_OPEN (from apps + // that enter PiP instantly on opening, mostly from CTS/Flicker tests) + if (transitType == TRANSIT_PIP || transitType == TRANSIT_OPEN) { + return true; + } + // This can happen if the request to enter PIP happens when we are collecting for + // another transition, such as TRANSIT_CHANGE (display rotation). + if (transitType == TRANSIT_CHANGE) { + return true; } + + // Please file a bug to handle the unexpected transition type. + throw new IllegalStateException("Entering PIP with unexpected transition type=" + + transitTypeToString(transitType)); } return false; } - private boolean startEnterAnimation(@NonNull TransitionInfo info, + private void startEnterAnimation(@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { - // Search for an Enter PiP transition (along with a show wallpaper one) + // Search for an Enter PiP transition TransitionInfo.Change enterPip = null; - TransitionInfo.Change wallpaper = null; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); if (change.getTaskInfo() != null && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_PINNED) { enterPip = change; - } else if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) { - wallpaper = change; } } if (enterPip == null) { - return false; - } - // Keep track of the PIP task. - mCurrentPipTaskToken = enterPip.getContainer(); - mHasFadeOut = false; - - if (mFinishCallback != null) { - callFinishCallback(null /* wct */); - mFinishTransaction = null; - throw new RuntimeException("Previous callback not called, aborting entering PIP."); + throw new IllegalStateException("Trying to start PiP animation without a pip" + + "participant"); } - // Show the wallpaper if there is a wallpaper change. - if (wallpaper != null) { - startTransaction.show(wallpaper.getLeash()); - startTransaction.setAlpha(wallpaper.getLeash(), 1.f); - } // Make sure other open changes are visible as entering PIP. Some may be hidden in // Transitions#setupStartState because the transition type is OPEN (such as auto-enter). for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); - if (change == enterPip || change == wallpaper) { - continue; - } + if (change == enterPip) continue; if (isOpeningType(change.getMode())) { final SurfaceControl leash = change.getLeash(); startTransaction.show(leash).setAlpha(leash, 1.f); } } + startEnterAnimation(enterPip, startTransaction, finishTransaction, finishCallback); + } + + @Override + public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange, + @NonNull final SurfaceControl.Transaction startTransaction, + @NonNull final SurfaceControl.Transaction finishTransaction, + @NonNull final Transitions.TransitionFinishCallback finishCallback) { + if (mFinishCallback != null) { + callFinishCallback(null /* wct */); + mFinishTransaction = null; + throw new RuntimeException("Previous callback not called, aborting entering PIP."); + } + + // Keep track of the PIP task and animation. + mCurrentPipTaskToken = pipChange.getContainer(); + mHasFadeOut = false; mPipTransitionState.setTransitionState(PipTransitionState.ENTERING_PIP); mFinishCallback = finishCallback; mFinishTransaction = finishTransaction; - final int endRotation = mInFixedRotation ? mEndFixedRotation : enterPip.getEndRotation(); - return startEnterAnimation(enterPip.getTaskInfo(), enterPip.getLeash(), - startTransaction, finishTransaction, enterPip.getStartRotation(), - endRotation); - } - private boolean startEnterAnimation(final TaskInfo taskInfo, final SurfaceControl leash, - final SurfaceControl.Transaction startTransaction, - final SurfaceControl.Transaction finishTransaction, - final int startRotation, final int endRotation) { + final ActivityManager.RunningTaskInfo taskInfo = pipChange.getTaskInfo(); + final SurfaceControl leash = pipChange.getLeash(); + final int startRotation = pipChange.getStartRotation(); + final int endRotation = mInFixedRotation ? mEndFixedRotation : pipChange.getEndRotation(); + setBoundsStateForEntry(taskInfo.topActivity, taskInfo.pictureInPictureParams, taskInfo.topActivityInfo); final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); @@ -657,7 +696,6 @@ public class PipTransition extends PipTransitionController { computeEnterPipRotatedBounds(rotationDelta, startRotation, endRotation, taskInfo, destinationBounds, sourceHintRect); } - PipAnimationController.PipTransitionAnimator animator; // Set corner radius for entering pip. mSurfaceTransactionHelper .crop(finishTransaction, leash, destinationBounds) @@ -694,7 +732,7 @@ public class PipTransition extends PipTransitionController { null /* callback */, false /* withStartDelay */); } mPipTransitionState.setInSwipePipToHomeTransition(false); - return true; + return; } if (rotationDelta != Surface.ROTATION_0) { @@ -702,6 +740,12 @@ public class PipTransition extends PipTransitionController { tmpTransform.postRotate(rotationDelta); startTransaction.setMatrix(leash, tmpTransform, new float[9]); } + if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { + startTransaction.setAlpha(leash, 0f); + } + startTransaction.apply(); + + PipAnimationController.PipTransitionAnimator animator; if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds, currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, @@ -712,7 +756,6 @@ public class PipTransition extends PipTransitionController { animator.setColorContentOverlay(mContext); } } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { - startTransaction.setAlpha(leash, 0f); animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds, 0f, 1f); mOneShotAnimationType = ANIM_TYPE_BOUNDS; @@ -720,7 +763,6 @@ public class PipTransition extends PipTransitionController { throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); } - startTransaction.apply(); animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration); @@ -731,8 +773,6 @@ public class PipTransition extends PipTransitionController { animator.setDestinationBounds(mPipBoundsAlgorithm.getEntryDestinationBounds()); } animator.start(); - - return true; } /** Computes destination bounds in old rotation and updates source hint rect if available. */ @@ -852,27 +892,4 @@ public class PipTransition extends PipTransitionController { mPipMenuController.movePipMenu(null, null, destinationBounds); mPipMenuController.updateMenuBounds(destinationBounds); } - - private void prepareFinishResizeTransaction(TaskInfo taskInfo, Rect destinationBounds, - @PipAnimationController.TransitionDirection int direction, - WindowContainerTransaction wct) { - Rect taskBounds = null; - if (isInPipDirection(direction)) { - // If we are animating from fullscreen using a bounds animation, then reset the - // activity windowing mode set by WM, and set the task bounds to the final bounds - taskBounds = destinationBounds; - wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); - wct.scheduleFinishEnterPip(taskInfo.token, destinationBounds); - } else if (isOutPipDirection(direction)) { - // If we are animating to fullscreen, then we need to reset the override bounds - // on the task to ensure that the task "matches" the parent's bounds. - taskBounds = (direction == TRANSITION_DIRECTION_LEAVE_PIP) - ? null : destinationBounds; - wct.setWindowingMode(taskInfo.token, getOutPipWindowingMode()); - // Simply reset the activity mode set prior to the animation running. - wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED); - } - - wct.setBounds(taskInfo.token, taskBounds); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 54f46e0c9938..d3f69f6762f9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -17,6 +17,7 @@ package com.android.wm.shell.pip; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.WindowManager.TRANSIT_PIP; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK; import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; @@ -28,10 +29,16 @@ import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.graphics.Rect; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.view.SurfaceControl; +import android.view.WindowManager; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; +import androidx.annotation.NonNull; + import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.transition.Transitions; @@ -206,6 +213,30 @@ public abstract class PipTransitionController implements Transitions.TransitionH return false; } + /** @return whether the transition-request represents a pip-entry. */ + public boolean requestHasPipEnter(@NonNull TransitionRequestInfo request) { + return request.getType() == TRANSIT_PIP; + } + + /** Whether a particular change is a window that is entering pip. */ + public boolean isEnteringPip(@NonNull TransitionInfo.Change change, + @WindowManager.TransitionType int transitType) { + return false; + } + + /** Add PiP-related changes to `outWCT` for the given request. */ + public void augmentRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) { + throw new IllegalStateException("Request isn't entering PiP"); + } + + /** Play a transition animation for entering PiP on a specific PiP change. */ + public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange, + @NonNull final SurfaceControl.Transaction startTransaction, + @NonNull final SurfaceControl.Transaction finishTransaction, + @NonNull final Transitions.TransitionFinishCallback finishCallback) { + } + /** * Callback interface for PiP transitions (both from and to PiP mode) */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index 4b1e5f8c0d7c..c3e6d82df781 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -110,7 +110,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb private PipAppOpsListener mAppOpsListener; private PipMediaController mMediaController; private PipBoundsAlgorithm mPipBoundsAlgorithm; + private PipKeepClearAlgorithm mPipKeepClearAlgorithm; private PipBoundsState mPipBoundsState; + private PipMotionHelper mPipMotionHelper; private PipTouchHandler mTouchHandler; private PipTransitionController mPipTransitionController; private TaskStackListenerImpl mTaskStackListener; @@ -156,7 +158,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb * Handler for display rotation changes. */ private final DisplayChangeController.OnDisplayChangingListener mRotationController = ( - int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) -> { + displayId, fromRotation, toRotation, newDisplayAreaInfo, t) -> { if (mPipTransitionController.handleRotateDisplay(fromRotation, toRotation, t)) { return; } @@ -286,7 +288,8 @@ public class PipController implements PipTransitionController.PipTransitionCallb @Nullable public static Pip create(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, - PipBoundsState pipBoundsState, PipMediaController pipMediaController, + PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState, + PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController, WindowManagerShellWrapper windowManagerShellWrapper, @@ -301,7 +304,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb } return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, - pipBoundsState, pipMediaController, + pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTouchHandler, pipTransitionController, windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder, oneHandedController, mainExecutor) @@ -312,7 +315,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, + PipKeepClearAlgorithm pipKeepClearAlgorithm, @NonNull PipBoundsState pipBoundsState, + PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, @@ -335,7 +340,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb mWindowManagerShellWrapper = windowManagerShellWrapper; mDisplayController = displayController; mPipBoundsAlgorithm = pipBoundsAlgorithm; + mPipKeepClearAlgorithm = pipKeepClearAlgorithm; mPipBoundsState = pipBoundsState; + mPipMotionHelper = pipMotionHelper; mPipTaskOrganizer = pipTaskOrganizer; mMainExecutor = mainExecutor; mMediaController = pipMediaController; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java new file mode 100644 index 000000000000..78084fafe197 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithm.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2022 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.wm.shell.pip.phone; + +import android.graphics.Rect; +import android.util.ArraySet; + +import com.android.wm.shell.pip.PipBoundsAlgorithm; +import com.android.wm.shell.pip.PipBoundsState; + +import java.util.Set; + +/** + * Calculates the adjusted position that does not occlude keep clear areas. + */ +public class PipKeepClearAlgorithm { + + /** + * Adjusts the current position of PiP to avoid occluding keep clear areas. If the user has + * moved PiP manually, the unmodified current position will be returned instead. + */ + public Rect adjust(PipBoundsState boundsState, PipBoundsAlgorithm boundsAlgorithm) { + if (boundsState.hasUserResizedPip()) { + return boundsState.getBounds(); + } + return adjust(boundsAlgorithm.getEntryDestinationBounds(), + boundsState.getRestrictedKeepClearAreas(), + boundsState.getUnrestrictedKeepClearAreas(), boundsState.getDisplayBounds()); + } + + /** Returns a new {@code Rect} that does not occlude the provided keep clear areas. */ + public Rect adjust(Rect defaultBounds, Set<Rect> restrictedKeepClearAreas, + Set<Rect> unrestrictedKeepClearAreas, Rect displayBounds) { + if (restrictedKeepClearAreas.isEmpty()) { + return defaultBounds; + } + Set<Rect> keepClearAreas = new ArraySet<>(); + if (!restrictedKeepClearAreas.isEmpty()) { + keepClearAreas.addAll(restrictedKeepClearAreas); + } + Rect outBounds = new Rect(defaultBounds); + for (Rect r : keepClearAreas) { + if (Rect.intersects(r, outBounds)) { + if (tryOffsetUp(outBounds, r, displayBounds)) continue; + if (tryOffsetLeft(outBounds, r, displayBounds)) continue; + if (tryOffsetDown(outBounds, r, displayBounds)) continue; + if (tryOffsetRight(outBounds, r, displayBounds)) continue; + } + } + return outBounds; + } + + private boolean tryOffsetLeft(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) { + return tryOffset(rectToMove, rectToAvoid, displayBounds, + rectToAvoid.left - rectToMove.right, 0); + } + + private boolean tryOffsetRight(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) { + return tryOffset(rectToMove, rectToAvoid, displayBounds, + rectToAvoid.right - rectToMove.left, 0); + } + + private boolean tryOffsetUp(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) { + return tryOffset(rectToMove, rectToAvoid, displayBounds, + 0, rectToAvoid.top - rectToMove.bottom); + } + + private boolean tryOffsetDown(Rect rectToMove, Rect rectToAvoid, Rect displayBounds) { + return tryOffset(rectToMove, rectToAvoid, displayBounds, + 0, rectToAvoid.bottom - rectToMove.top); + } + + private boolean tryOffset(Rect rectToMove, Rect rectToAvoid, Rect displayBounds, + int dx, int dy) { + Rect tmp = new Rect(rectToMove); + tmp.offset(dx, dy); + if (!Rect.intersects(rectToAvoid, tmp) && displayBounds.contains(tmp)) { + rectToMove.offsetTo(tmp.left, tmp.top); + return true; + } + return false; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 6390c8984dac..1958157fc319 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -282,7 +282,7 @@ public class PipMenuView extends FrameLayout { && mSplitScreenControllerOptional.get().isTaskInSplitScreen(taskInfo.taskId); mFocusedTaskAllowSplitScreen = isSplitScreen || (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN - && taskInfo.supportsSplitScreenMultiWindow + && taskInfo.supportsMultiWindow && taskInfo.topActivityType != WindowConfiguration.ACTIVITY_TYPE_HOME); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index ac7b9033b2b9..a2ff97247189 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -54,6 +54,7 @@ import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipTaskOrganizer; +import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.protolog.ShellProtoLogGroup; @@ -250,6 +251,10 @@ public class PipTouchHandler { }); } + public PipTransitionController getTransitionHandler() { + return mPipTaskOrganizer.getTransitionController(); + } + private void reloadResources() { final Resources res = mContext.getResources(); mBottomOffsetBufferPx = res.getDimensionPixelSize(R.dimen.pip_bottom_offset_buffer); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index 31b510c38457..0d976d4a81eb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -108,7 +108,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, static final int EXIT_REASON_ROOT_TASK_VANISHED = 6; static final int EXIT_REASON_SCREEN_LOCKED = 7; static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8; - static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9; + public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9; @IntDef(value = { EXIT_REASON_UNKNOWN, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, @@ -198,6 +198,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mStageCoordinator.isSplitScreenVisible(); } + public StageCoordinator getTransitionHandler() { + return mStageCoordinator; + } + @Nullable public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) { if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index cd121ed41fdd..f7057d454df9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -21,7 +21,6 @@ import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; -import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM; import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; @@ -58,9 +57,6 @@ import java.util.ArrayList; class SplitScreenTransitions { private static final String TAG = "SplitScreenTransitions"; - /** Flag applied to a transition change to identify it as a divider bar for animation. */ - public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM; - private final TransactionPool mTransactionPool; private final Transitions mTransitions; private final Runnable mOnFinish; @@ -70,8 +66,9 @@ class SplitScreenTransitions { IBinder mPendingRecent = null; private IBinder mAnimatingTransition = null; - private OneShotRemoteHandler mPendingRemoteHandler = null; + OneShotRemoteHandler mPendingRemoteHandler = null; private OneShotRemoteHandler mActiveRemoteHandler = null; + private boolean mEnterTransitionMerged; private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish; @@ -94,7 +91,8 @@ class SplitScreenTransitions { @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback, - @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) { + @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot, + @NonNull WindowContainerToken topRoot) { mFinishCallback = finishCallback; mAnimatingTransition = transition; if (mPendingRemoteHandler != null) { @@ -104,12 +102,12 @@ class SplitScreenTransitions { mPendingRemoteHandler = null; return; } - playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot); + playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot); } private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot, - @NonNull WindowContainerToken sideRoot) { + @NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) { mFinishTransaction = mTransactionPool.acquire(); // Play some place-holder fade animations @@ -140,7 +138,10 @@ class SplitScreenTransitions { endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y); startExampleResizeAnimation(leash, startBounds, endBounds); } - if (change.getParent() != null) { + boolean isRootOrSplitSideRoot = change.getParent() == null + || topRoot.equals(change.getParent()); + // For enter or exit, we only want to animate the side roots but not the top-root. + if (!isRootOrSplitSideRoot || topRoot.equals(change.getContainer())) { continue; } @@ -187,27 +188,28 @@ class SplitScreenTransitions { } /** Starts a transition to dismiss split. */ - IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct, + IBinder startDismissTransition(WindowContainerTransaction wct, Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop, @SplitScreenController.ExitReason int reason) { final int type = reason == EXIT_REASON_DRAG_DIVIDER ? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS; - if (transition == null) { - transition = mTransitions.startTransition(type, wct, handler); - } + IBinder transition = mTransitions.startTransition(type, wct, handler); + setDismissTransition(transition, dismissTop, reason); + return transition; + } + + /** Sets a transition to dismiss split. */ + void setDismissTransition(@NonNull IBinder transition, @SplitScreen.StageType int dismissTop, + @SplitScreenController.ExitReason int reason) { mPendingDismiss = new DismissTransition(transition, reason, dismissTop); ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + " deduced Dismiss due to %s. toTop=%s", exitReasonToString(reason), stageTypeToString(dismissTop)); - return transition; } - IBinder startRecentTransition(@Nullable IBinder transition, WindowContainerTransaction wct, - Transitions.TransitionHandler handler, @Nullable RemoteTransition remoteTransition) { - if (transition == null) { - transition = mTransitions.startTransition(TRANSIT_OPEN, wct, handler); - } + void setRecentTransition(@NonNull IBinder transition, + @Nullable RemoteTransition remoteTransition) { mPendingRecent = transition; if (remoteTransition != null) { @@ -219,7 +221,6 @@ class SplitScreenTransitions { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " + " deduced Enter recent panel"); - return transition; } void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, @@ -229,6 +230,18 @@ class SplitScreenTransitions { } } + void onTransitionMerged(@NonNull IBinder transition) { + // Once a pending enter transition got merged, make sure to append the reset of finishing + // operations to the finish transition. + if (transition == mPendingEnter) { + mFinishTransaction = mTransactionPool.acquire(); + mStageCoordinator.finishEnterSplitScreen(mFinishTransaction); + mPendingEnter = null; + mPendingRemoteHandler = null; + mEnterTransitionMerged = true; + } + } + void onFinish(WindowContainerTransaction wct, WindowContainerTransactionCallback wctCB) { if (!mAnimations.isEmpty()) return; if (mAnimatingTransition == mPendingEnter) { @@ -238,18 +251,16 @@ class SplitScreenTransitions { mPendingDismiss = null; } if (mAnimatingTransition == mPendingRecent) { - // If the clean-up wct is null when finishing recent transition, it indicates it's - // returning to home and thus no need to reorder tasks. - final boolean returnToHome = wct == null; - if (returnToHome) { - wct = new WindowContainerTransaction(); + if (!mEnterTransitionMerged) { + if (wct == null) wct = new WindowContainerTransaction(); + mStageCoordinator.onRecentTransitionFinished(wct, mFinishTransaction); } - mStageCoordinator.onRecentTransitionFinished(returnToHome, wct, mFinishTransaction); mPendingRecent = null; } mPendingRemoteHandler = null; mActiveRemoteHandler = null; mAnimatingTransition = null; + mEnterTransitionMerged = false; mOnFinish.run(); if (mFinishTransaction != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 7ea32a6d8f86..f625387a05d5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -28,7 +28,9 @@ import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.transitTypeToString; +import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -47,7 +49,6 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP; import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN; import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString; -import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; @@ -83,6 +84,7 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.WindowManager; +import android.window.DisplayAreaInfo; import android.window.RemoteTransition; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; @@ -131,12 +133,15 @@ import javax.inject.Provider; * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and * {@link #onStageHasChildrenChanged(StageListenerImpl).} */ -class StageCoordinator implements SplitLayout.SplitLayoutHandler, +public class StageCoordinator implements SplitLayout.SplitLayoutHandler, DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler, ShellTaskOrganizer.TaskListener { private static final String TAG = StageCoordinator.class.getSimpleName(); + /** Flag applied to a transition change to identify it as a divider bar for animation. */ + public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM; + private final SurfaceSession mSurfaceSession = new SurfaceSession(); private final MainStage mMainStage; @@ -365,10 +370,15 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, sideOptions = sideOptions != null ? sideOptions : new Bundle(); setSideStagePosition(sidePosition, wct); + if (mMainStage.isActive()) { + mMainStage.evictAllChildren(wct); + mSideStage.evictAllChildren(wct); + } else { + // Build a request WCT that will launch both apps such that task 0 is on the main stage + // while task 1 is on the side stage. + mMainStage.activate(wct, false /* reparent */); + } mSplitLayout.setDivideRatio(splitRatio); - // Build a request WCT that will launch both apps such that task 0 is on the main stage - // while task 1 is on the side stage. - mMainStage.activate(wct, false /* reparent */); updateWindowBounds(mSplitLayout, wct); wct.reorder(mRootTaskInfo.token, true); @@ -641,7 +651,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (ENABLE_SHELL_TRANSITIONS) { final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(mTopStageAfterFoldDismiss, wct); - mSplitTransitions.startDismissTransition(null /* transition */, wct, this, + mSplitTransitions.startDismissTransition(wct, this, mTopStageAfterFoldDismiss, EXIT_REASON_DEVICE_FOLDED); } else { exitSplitScreen( @@ -674,8 +684,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, final int dismissTop = mainStageVisible ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(dismissTop, wct); - mSplitTransitions.startDismissTransition(null /* transition */, wct, this, - dismissTop, EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP); + mSplitTransitions.startDismissTransition(wct, this, dismissTop, + EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP); } } } @@ -738,10 +748,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, setDividerVisibility(false, t); }); - // Hide divider and reset its position. - mSplitLayout.resetDividerPosition(); - mSplitLayout.release(); - mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; + onTransitionAnimationComplete(); Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason)); // Log the exit if (childrenToTop != null) { @@ -1024,8 +1031,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, wct.reparent(mMainStage.mRootTaskInfo.token, mRootTaskInfo.token, true); wct.reparent(mSideStage.mRootTaskInfo.token, mRootTaskInfo.token, true); // Make the stages adjacent to each other so they occlude what's behind them. - wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token, - true /* moveTogether */); + wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); mTaskOrganizer.applyTransaction(wct); } @@ -1202,8 +1208,10 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(dismissTop, wct); - mSplitTransitions.startDismissTransition( - null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER); + if (mRootTaskInfo != null) { + wct.setDoNotPip(mRootTaskInfo.token); + } + mSplitTransitions.startDismissTransition(wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER); } @Override @@ -1321,7 +1329,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (displayId != DEFAULT_DISPLAY) { return; } - mDisplayController.addDisplayChangingController(this::onRotateDisplay); + mDisplayController.addDisplayChangingController(this::onDisplayChange); } @Override @@ -1332,14 +1340,17 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDisplayLayout.set(mDisplayController.getDisplayLayout(displayId)); } - private void onRotateDisplay(int displayId, int fromRotation, int toRotation, - WindowContainerTransaction wct) { + private void onDisplayChange(int displayId, int fromRotation, int toRotation, + @Nullable DisplayAreaInfo newDisplayAreaInfo, WindowContainerTransaction wct) { if (!mMainStage.isActive()) return; // Only do this when shell transition if (!ENABLE_SHELL_TRANSITIONS) return; mDisplayLayout.rotateTo(mContext.getResources(), toRotation); mSplitLayout.rotateTo(toRotation, mDisplayLayout.stableInsets()); + if (newDisplayAreaInfo != null) { + mSplitLayout.updateConfiguration(newDisplayAreaInfo.configuration); + } updateWindowBounds(mSplitLayout, wct); updateUnfoldBounds(); } @@ -1392,7 +1403,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Nullable TransitionRequestInfo request) { final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); if (triggerTask == null) { - if (mMainStage.isActive()) { + if (isSplitActive()) { + // Check if the display is rotating. final TransitionRequestInfo.DisplayChange displayChange = request.getDisplayChange(); if (request.getType() == TRANSIT_CHANGE && displayChange != null @@ -1419,7 +1431,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId)); } - if (mMainStage.isActive()) { + if (isSplitActive()) { // Try to handle everything while in split-screen, so return a WCT even if it's empty. ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split" + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d" @@ -1434,7 +1446,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; prepareExitSplitScreen(dismissTop, out); - mSplitTransitions.startDismissTransition(transition, out, this, dismissTop, + mSplitTransitions.setDismissTransition(transition, dismissTop, EXIT_REASON_APP_FINISHED); } } else if (isOpening && inFullscreen) { @@ -1444,12 +1456,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } else if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) { // Enter overview panel, so start recent transition. - mSplitTransitions.startRecentTransition(transition, out, this, + mSplitTransitions.setRecentTransition(transition, request.getRemoteTransition()); - } else { - // Occluded by the other fullscreen task, so dismiss both. + } else if (mSplitTransitions.mPendingRecent == null) { + // If split-task is not controlled by recents animation + // and occluded by the other fullscreen task, dismiss both. prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out); - mSplitTransitions.startDismissTransition(transition, out, this, + mSplitTransitions.setDismissTransition(transition, STAGE_TYPE_UNDEFINED, EXIT_REASON_UNKNOWN); } } @@ -1464,6 +1477,33 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return out; } + /** + * This is used for mixed scenarios. For such scenarios, just make sure to include exiting + * split or entering split when appropriate. + */ + public void addEnterOrExitIfNeeded(@Nullable TransitionRequestInfo request, + @NonNull WindowContainerTransaction outWCT) { + final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); + if (triggerTask != null && triggerTask.displayId != mDisplayId) { + // Skip handling task on the other display. + return; + } + final @WindowManager.TransitionType int type = request.getType(); + if (isSplitActive() && !isOpeningType(type) + && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " One of the splits became " + + "empty during a mixed transition (one not handled by split)," + + " so make sure split-screen state is cleaned-up. " + + "mainStageCount=%d sideStageCount=%d", mMainStage.getChildCount(), + mSideStage.getChildCount()); + prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, outWCT); + } + } + + public boolean isSplitActive() { + return mMainStage.isActive(); + } + @Override public void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, IBinder mergeTarget, @@ -1473,15 +1513,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onTransitionMerged(@NonNull IBinder transition) { - // Once the pending enter transition got merged, make sure to bring divider bar visible and - // clear the pending transition from cache to prevent mess-up the following state. - if (transition == mSplitTransitions.mPendingEnter) { - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - finishEnterSplitScreen(t); - mSplitTransitions.mPendingEnter = null; - t.apply(); - mTransactionPool.release(t); - } + mSplitTransitions.onTransitionMerged(transition); } @Override @@ -1552,11 +1584,13 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, if (!shouldAnimate) return false; mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction, - finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); + finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token, + mRootTaskInfo.token); return true; } - void onTransitionAnimationComplete() { + /** Called to clean-up state and do house-keeping after the animation is done. */ + public void onTransitionAnimationComplete() { // If still playing, let it finish. if (!mMainStage.isActive()) { // Update divider state after animation so that it is still around and positioned @@ -1620,8 +1654,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return true; } - private boolean startPendingDismissAnimation( - @NonNull SplitScreenTransitions.DismissTransition dismissTransition, + /** Synchronize split-screen state with transition and make appropriate preparations. */ + public void prepareDismissAnimation(@StageType int toStage, @ExitReason int dismissReason, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) { // Make some noise if things aren't totally expected. These states shouldn't effect @@ -1654,7 +1688,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mRecentTasks.ifPresent(recentTasks -> { // Notify recents if we are exiting in a way that breaks the pair, and disable further // updates to splits in the recents until we enter split again - if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) { + if (shouldBreakPairedTaskInRecents(dismissReason) && mShouldUpdateRecents) { for (TransitionInfo.Change change : info.getChanges()) { final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); if (taskInfo != null @@ -1671,30 +1705,37 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Wait until after animation to update divider // Reset crops so they don't interfere with subsequent launches - t.setWindowCrop(mMainStage.mRootLeash, null); - t.setWindowCrop(mSideStage.mRootLeash, null); + t.setCrop(mMainStage.mRootLeash, null); + t.setCrop(mSideStage.mRootLeash, null); + + if (toStage == STAGE_TYPE_UNDEFINED) { + logExit(dismissReason); + } else { + logExitToStage(dismissReason, toStage == STAGE_TYPE_MAIN); + } + // Hide divider and dim layer on transition finished. + setDividerVisibility(false, finishT); + finishT.hide(mMainStage.mDimLayer); + finishT.hide(mSideStage.mDimLayer); + } + + private boolean startPendingDismissAnimation( + @NonNull SplitScreenTransitions.DismissTransition dismissTransition, + @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.Transaction finishT) { + prepareDismissAnimation(dismissTransition.mDismissTop, dismissTransition.mReason, info, + t, finishT); if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) { - logExit(dismissTransition.mReason); // TODO: Have a proper remote for this. Until then, though, reset state and use the // normal animation stuff (which falls back to the normal launcher remote). + t.hide(mSplitLayout.getDividerLeash()); mSplitLayout.release(t); mSplitTransitions.mPendingDismiss = null; return false; - } else { - logExitToStage(dismissTransition.mReason, - dismissTransition.mDismissTop == STAGE_TYPE_MAIN); } addDividerBarToTransition(info, t, false /* show */); - // We're dismissing split by moving the other one to fullscreen. - // Since we don't have any animations for this yet, just use the internal example - // animations. - - // Hide divider and dim layer on transition finished. - setDividerVisibility(false, finishT); - finishT.hide(mMainStage.mDimLayer); - finishT.hide(mSideStage.mDimLayer); return true; } @@ -1704,26 +1745,26 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, return true; } - void onRecentTransitionFinished(boolean returnToHome, WindowContainerTransaction wct, + void onRecentTransitionFinished(WindowContainerTransaction wct, SurfaceControl.Transaction finishT) { - // Exclude the case that the split screen has been dismissed already. - if (!mMainStage.isActive()) { - // The latest split dismissing transition might be a no-op transition and thus won't - // callback startAnimation, update split visibility here to cover this kind of no-op - // transition case. - setSplitsVisible(false); - return; + // Check if the recent transition is finished by returning to the current split so we can + // restore the divider bar. + for (int i = 0; i < wct.getHierarchyOps().size(); ++i) { + final WindowContainerTransaction.HierarchyOp op = wct.getHierarchyOps().get(i); + final IBinder container = op.getContainer(); + if (op.getType() == HIERARCHY_OP_TYPE_REORDER && op.getToTop() + && (mMainStage.containsContainer(container) + || mSideStage.containsContainer(container))) { + setDividerVisibility(true, finishT); + return; + } } - if (returnToHome) { - // When returning to home from recent apps, the splitting tasks are already hidden, so - // append the reset of dismissing operations into the clean-up wct. - prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); - setSplitsVisible(false); - logExit(EXIT_REASON_RETURN_HOME); - } else { - setDividerVisibility(true, finishT); - } + // Dismiss the split screen is it's not returning to split. + prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); + setSplitsVisible(false); + setDividerVisibility(false, finishT); + logExit(EXIT_REASON_UNKNOWN); } private void addDividerBarToTransition(@NonNull TransitionInfo info, @@ -1870,8 +1911,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, final int stageType = isMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(stageType, wct); - mSplitTransitions.startDismissTransition(null /* transition */, wct, - StageCoordinator.this, stageType, + mSplitTransitions.startDismissTransition(wct,StageCoordinator.this, stageType, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 949bf5f55808..f9dd7c96294f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -31,6 +31,7 @@ import android.app.ActivityManager; import android.content.Context; import android.graphics.Point; import android.graphics.Rect; +import android.os.IBinder; import android.util.SparseArray; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -47,6 +48,7 @@ import com.android.wm.shell.common.split.SplitDecorManager; import com.android.wm.shell.splitscreen.SplitScreen.StageType; import java.io.PrintWriter; +import java.util.function.Predicate; /** * Base class that handle common task org. related for split-screen stages. @@ -119,63 +121,53 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } boolean containsToken(WindowContainerToken token) { - if (token.equals(mRootTaskInfo.token)) { - return true; - } - - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - if (token.equals(mChildrenTaskInfo.valueAt(i).token)) { - return true; - } - } + return contains(t -> t.token.equals(token)); + } - return false; + boolean containsContainer(IBinder binder) { + return contains(t -> t.token.asBinder() == binder); } /** * Returns the top visible child task's id. */ int getTopVisibleChildTaskId() { - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i); - if (info.isVisible) { - return info.taskId; - } - } - return INVALID_TASK_ID; + final ActivityManager.RunningTaskInfo taskInfo = getChildTaskInfo(t -> t.isVisible); + return taskInfo != null ? taskInfo.taskId : INVALID_TASK_ID; } /** * Returns the top activity uid for the top child task. */ int getTopChildTaskUid() { - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i); - if (info.topActivityInfo == null) { - continue; - } - return info.topActivityInfo.applicationInfo.uid; - } - return 0; + final ActivityManager.RunningTaskInfo taskInfo = + getChildTaskInfo(t -> t.topActivityInfo != null); + return taskInfo != null ? taskInfo.topActivityInfo.applicationInfo.uid : 0; } /** @return {@code true} if this listener contains the currently focused task. */ boolean isFocused() { - if (mRootTaskInfo == null) { - return false; - } + return contains(t -> t.isFocused); + } - if (mRootTaskInfo.isFocused) { + private boolean contains(Predicate<ActivityManager.RunningTaskInfo> predicate) { + if (mRootTaskInfo != null && predicate.test(mRootTaskInfo)) { return true; } + return getChildTaskInfo(predicate) != null; + } + + @Nullable + private ActivityManager.RunningTaskInfo getChildTaskInfo( + Predicate<ActivityManager.RunningTaskInfo> predicate) { for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - if (mChildrenTaskInfo.valueAt(i).isFocused) { - return true; + final ActivityManager.RunningTaskInfo taskInfo = mChildrenTaskInfo.valueAt(i); + if (predicate.test(taskInfo)) { + return taskInfo; } } - - return false; + return null; } @Override @@ -266,13 +258,13 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { mChildrenTaskInfo.remove(taskId); mChildrenLeashes.remove(taskId); mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible); - if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) { - mCallbacks.onChildTaskEnterPip(taskId); - } if (ENABLE_SHELL_TRANSITIONS) { // Status is managed/synchronized by the transition lifecycle. return; } + if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) { + mCallbacks.onChildTaskEnterPip(taskId); + } sendStatusChanged(); } else { throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreen.aidl deleted file mode 100644 index 45f6d3c8b154..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreen.aidl +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.stagesplit; - -import android.app.PendingIntent; -import android.content.Intent; -import android.os.Bundle; -import android.os.UserHandle; -import android.view.RemoteAnimationAdapter; -import android.view.RemoteAnimationTarget; -import android.window.RemoteTransition; - -import com.android.wm.shell.stagesplit.ISplitScreenListener; - -/** - * Interface that is exposed to remote callers to manipulate the splitscreen feature. - */ -interface ISplitScreen { - - /** - * Registers a split screen listener. - */ - oneway void registerSplitScreenListener(in ISplitScreenListener listener) = 1; - - /** - * Unregisters a split screen listener. - */ - oneway void unregisterSplitScreenListener(in ISplitScreenListener listener) = 2; - - /** - * Hides the side-stage if it is currently visible. - */ - oneway void setSideStageVisibility(boolean visible) = 3; - - /** - * Removes a task from the side stage. - */ - oneway void removeFromSideStage(int taskId) = 4; - - /** - * Removes the split-screen stages and leaving indicated task to top. Passing INVALID_TASK_ID - * to indicate leaving no top task after leaving split-screen. - */ - oneway void exitSplitScreen(int toTopTaskId) = 5; - - /** - * @param exitSplitScreenOnHide if to exit split-screen if both stages are not visible. - */ - oneway void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) = 6; - - /** - * Starts a task in a stage. - */ - oneway void startTask(int taskId, int stage, int position, in Bundle options) = 7; - - /** - * Starts a shortcut in a stage. - */ - oneway void startShortcut(String packageName, String shortcutId, int stage, int position, - in Bundle options, in UserHandle user) = 8; - - /** - * Starts an activity in a stage. - */ - oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int stage, - int position, in Bundle options) = 9; - - /** - * Starts tasks simultaneously in one transition. - */ - oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId, - in Bundle sideOptions, int sidePosition, in RemoteTransition remoteTransition) = 10; - - /** - * Version of startTasks using legacy transition system. - */ - oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions, - int sideTaskId, in Bundle sideOptions, int sidePosition, - in RemoteAnimationAdapter adapter) = 11; - - /** - * Blocking call that notifies and gets additional split-screen targets when entering - * recents (for example: the dividerBar). - * @param cancel is true if leaving recents back to split (eg. the gesture was cancelled). - * @param appTargets apps that will be re-parented to display area - */ - RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, - in RemoteAnimationTarget[] appTargets) = 12; -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/MainStage.java deleted file mode 100644 index 83855be91e04..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/MainStage.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.stagesplit; - -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; - -import android.annotation.Nullable; -import android.graphics.Rect; -import android.view.SurfaceSession; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.SyncTransactionQueue; - -/** - * Main stage for split-screen mode. When split-screen is active all standard activity types launch - * on the main stage, except for task that are explicitly pinned to the {@link SideStage}. - * @see StageCoordinator - */ -class MainStage extends StageTaskListener { - private static final String TAG = MainStage.class.getSimpleName(); - - private boolean mIsActive = false; - - MainStage(ShellTaskOrganizer taskOrganizer, int displayId, - StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, - @Nullable StageTaskUnfoldController stageTaskUnfoldController) { - super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, - stageTaskUnfoldController); - } - - boolean isActive() { - return mIsActive; - } - - void activate(Rect rootBounds, WindowContainerTransaction wct) { - if (mIsActive) return; - - final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.setBounds(rootToken, rootBounds) - .setWindowingMode(rootToken, WINDOWING_MODE_MULTI_WINDOW) - .setLaunchRoot( - rootToken, - CONTROLLED_WINDOWING_MODES, - CONTROLLED_ACTIVITY_TYPES) - .reparentTasks( - null /* currentParent */, - rootToken, - CONTROLLED_WINDOWING_MODES, - CONTROLLED_ACTIVITY_TYPES, - true /* onTop */) - // Moving the root task to top after the child tasks were re-parented , or the root - // task cannot be visible and focused. - .reorder(rootToken, true /* onTop */); - - mIsActive = true; - } - - void deactivate(WindowContainerTransaction wct) { - deactivate(wct, false /* toTop */); - } - - void deactivate(WindowContainerTransaction wct, boolean toTop) { - if (!mIsActive) return; - mIsActive = false; - - if (mRootTaskInfo == null) return; - final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.setLaunchRoot( - rootToken, - null, - null) - .reparentTasks( - rootToken, - null /* newParent */, - CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, - CONTROLLED_ACTIVITY_TYPES, - toTop) - // We want this re-order to the bottom regardless since we are re-parenting - // all its tasks. - .reorder(rootToken, false /* onTop */); - } - - void updateConfiguration(int windowingMode, Rect bounds, WindowContainerTransaction wct) { - wct.setBounds(mRootTaskInfo.token, bounds) - .setWindowingMode(mRootTaskInfo.token, windowingMode); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS deleted file mode 100644 index 264e88f32bff..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# WM shell sub-modules stagesplit owner -chenghsiuchang@google.com diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineManager.java deleted file mode 100644 index 8fbad52c630f..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineManager.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.stagesplit; - -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import android.annotation.Nullable; -import android.content.Context; -import android.content.res.Configuration; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.os.Binder; -import android.view.IWindow; -import android.view.InsetsSource; -import android.view.InsetsState; -import android.view.LayoutInflater; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.WindowlessWindowManager; -import android.widget.FrameLayout; - -import com.android.wm.shell.R; - -/** - * Handles drawing outline of the bounds of provided root surface. The outline will be drown with - * the consideration of display insets like status bar, navigation bar and display cutout. - */ -class OutlineManager extends WindowlessWindowManager { - private static final String WINDOW_NAME = "SplitOutlineLayer"; - private final Context mContext; - private final Rect mRootBounds = new Rect(); - private final Rect mTempRect = new Rect(); - private final Rect mLastOutlineBounds = new Rect(); - private final InsetsState mInsetsState = new InsetsState(); - private final int mExpandedTaskBarHeight; - private OutlineView mOutlineView; - private SurfaceControlViewHost mViewHost; - private SurfaceControl mHostLeash; - private SurfaceControl mLeash; - - OutlineManager(Context context, Configuration configuration) { - super(configuration, null /* rootSurface */, null /* hostInputToken */); - mContext = context.createWindowContext(context.getDisplay(), TYPE_APPLICATION_OVERLAY, - null /* options */); - mExpandedTaskBarHeight = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.taskbar_frame_height); - } - - @Override - protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { - b.setParent(mHostLeash); - } - - void inflate(SurfaceControl rootLeash, Rect rootBounds) { - if (mLeash != null || mViewHost != null) return; - - mHostLeash = rootLeash; - mRootBounds.set(rootBounds); - mViewHost = new SurfaceControlViewHost(mContext, mContext.getDisplay(), this); - - final FrameLayout rootLayout = (FrameLayout) LayoutInflater.from(mContext) - .inflate(R.layout.split_outline, null); - mOutlineView = rootLayout.findViewById(R.id.split_outline); - - final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - 0 /* width */, 0 /* height */, TYPE_APPLICATION_OVERLAY, - FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE, PixelFormat.TRANSLUCENT); - lp.width = mRootBounds.width(); - lp.height = mRootBounds.height(); - lp.token = new Binder(); - lp.setTitle(WINDOW_NAME); - lp.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY; - // TODO(b/189839391): Set INPUT_FEATURE_NO_INPUT_CHANNEL after WM supports - // TRUSTED_OVERLAY for windowless window without input channel. - mViewHost.setView(rootLayout, lp); - mLeash = getSurfaceControl(mViewHost.getWindowToken()); - - drawOutline(); - } - - void release() { - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - mRootBounds.setEmpty(); - mLastOutlineBounds.setEmpty(); - mOutlineView = null; - mHostLeash = null; - mLeash = null; - } - - @Nullable - SurfaceControl getOutlineLeash() { - return mLeash; - } - - void setVisibility(boolean visible) { - if (mOutlineView != null) { - mOutlineView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - } - } - - void setRootBounds(Rect rootBounds) { - if (mViewHost == null || mViewHost.getView() == null) { - return; - } - - if (!mRootBounds.equals(rootBounds)) { - WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams(); - lp.width = rootBounds.width(); - lp.height = rootBounds.height(); - mViewHost.relayout(lp); - mRootBounds.set(rootBounds); - drawOutline(); - } - } - - void onInsetsChanged(InsetsState insetsState) { - if (!mInsetsState.equals(insetsState)) { - mInsetsState.set(insetsState); - drawOutline(); - } - } - - private void computeOutlineBounds(Rect rootBounds, InsetsState insetsState, Rect outBounds) { - outBounds.set(rootBounds); - final InsetsSource taskBarInsetsSource = - insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); - // Only insets the divider bar with task bar when it's expanded so that the rounded corners - // will be drawn against task bar. - if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { - outBounds.inset(taskBarInsetsSource.calculateVisibleInsets(outBounds)); - } - - // Offset the coordinate from screen based to surface based. - outBounds.offset(-rootBounds.left, -rootBounds.top); - } - - void drawOutline() { - if (mOutlineView == null) { - return; - } - - computeOutlineBounds(mRootBounds, mInsetsState, mTempRect); - if (mTempRect.equals(mLastOutlineBounds)) { - return; - } - - ViewGroup.MarginLayoutParams lp = - (ViewGroup.MarginLayoutParams) mOutlineView.getLayoutParams(); - lp.leftMargin = mTempRect.left; - lp.topMargin = mTempRect.top; - lp.width = mTempRect.width(); - lp.height = mTempRect.height(); - mOutlineView.setLayoutParams(lp); - mLastOutlineBounds.set(mTempRect); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineView.java deleted file mode 100644 index 92b1381fc808..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/OutlineView.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.stagesplit; - -import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT; -import static android.view.RoundedCorner.POSITION_BOTTOM_RIGHT; -import static android.view.RoundedCorner.POSITION_TOP_LEFT; -import static android.view.RoundedCorner.POSITION_TOP_RIGHT; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Path; -import android.util.AttributeSet; -import android.view.RoundedCorner; -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.R; - -/** View for drawing split outline. */ -public class OutlineView extends View { - private final Paint mPaint = new Paint(); - private final Path mPath = new Path(); - private final float[] mRadii = new float[8]; - - public OutlineView(@NonNull Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setStrokeWidth( - getResources().getDimension(R.dimen.accessibility_focus_highlight_stroke_width)); - mPaint.setColor(getResources().getColor(R.color.system_accent1_100, null)); - } - - @Override - protected void onAttachedToWindow() { - // TODO(b/200850654): match the screen corners with the actual display decor. - mRadii[0] = mRadii[1] = getCornerRadius(POSITION_TOP_LEFT); - mRadii[2] = mRadii[3] = getCornerRadius(POSITION_TOP_RIGHT); - mRadii[4] = mRadii[5] = getCornerRadius(POSITION_BOTTOM_RIGHT); - mRadii[6] = mRadii[7] = getCornerRadius(POSITION_BOTTOM_LEFT); - } - - private int getCornerRadius(@RoundedCorner.Position int position) { - final RoundedCorner roundedCorner = getDisplay().getRoundedCorner(position); - return roundedCorner == null ? 0 : roundedCorner.getRadius(); - } - - @Override - protected void onLayout(boolean changed, int left, int top, int right, int bottom) { - if (changed) { - mPath.reset(); - mPath.addRoundRect(0, 0, getWidth(), getHeight(), mRadii, Path.Direction.CW); - } - } - - @Override - protected void onDraw(Canvas canvas) { - canvas.drawPath(mPath, mPaint); - } - - @Override - public boolean hasOverlappingRendering() { - return false; - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SideStage.java deleted file mode 100644 index 55c4f3aea19a..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SideStage.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.stagesplit; - -import android.annotation.CallSuper; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.content.Context; -import android.graphics.Rect; -import android.view.InsetsSourceControl; -import android.view.InsetsState; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.SyncTransactionQueue; - -/** - * Side stage for split-screen mode. Only tasks that are explicitly pinned to this stage show up - * here. All other task are launch in the {@link MainStage}. - * - * @see StageCoordinator - */ -class SideStage extends StageTaskListener implements - DisplayInsetsController.OnInsetsChangedListener { - private static final String TAG = SideStage.class.getSimpleName(); - private final Context mContext; - private OutlineManager mOutlineManager; - - SideStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId, - StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, - @Nullable StageTaskUnfoldController stageTaskUnfoldController) { - super(taskOrganizer, displayId, callbacks, syncQueue, surfaceSession, - stageTaskUnfoldController); - mContext = context; - } - - void addTask(ActivityManager.RunningTaskInfo task, Rect rootBounds, - WindowContainerTransaction wct) { - final WindowContainerToken rootToken = mRootTaskInfo.token; - wct.setBounds(rootToken, rootBounds) - .reparent(task.token, rootToken, true /* onTop*/) - // Moving the root task to top after the child tasks were reparented , or the root - // task cannot be visible and focused. - .reorder(rootToken, true /* onTop */); - } - - boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { - // No matter if the root task is empty or not, moving the root to bottom because it no - // longer preserves visible child task. - wct.reorder(mRootTaskInfo.token, false /* onTop */); - if (mChildrenTaskInfo.size() == 0) return false; - wct.reparentTasks( - mRootTaskInfo.token, - null /* newParent */, - CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, - CONTROLLED_ACTIVITY_TYPES, - toTop); - return true; - } - - boolean removeTask(int taskId, WindowContainerToken newParent, WindowContainerTransaction wct) { - final ActivityManager.RunningTaskInfo task = mChildrenTaskInfo.get(taskId); - if (task == null) return false; - wct.reparent(task.token, newParent, false /* onTop */); - return true; - } - - @Nullable - public SurfaceControl getOutlineLeash() { - return mOutlineManager.getOutlineLeash(); - } - - @Override - @CallSuper - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - super.onTaskAppeared(taskInfo, leash); - if (isRootTask(taskInfo)) { - mOutlineManager = new OutlineManager(mContext, taskInfo.configuration); - enableOutline(true); - } - } - - @Override - @CallSuper - public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - super.onTaskInfoChanged(taskInfo); - if (isRootTask(taskInfo)) { - mOutlineManager.setRootBounds(taskInfo.configuration.windowConfiguration.getBounds()); - } - } - - private boolean isRootTask(ActivityManager.RunningTaskInfo taskInfo) { - return mRootTaskInfo != null && mRootTaskInfo.taskId == taskInfo.taskId; - } - - void enableOutline(boolean enable) { - if (mOutlineManager == null) { - return; - } - - if (enable) { - if (mRootTaskInfo != null) { - mOutlineManager.inflate(mRootLeash, - mRootTaskInfo.configuration.windowConfiguration.getBounds()); - } - } else { - mOutlineManager.release(); - } - } - - void setOutlineVisibility(boolean visible) { - mOutlineManager.setVisibility(visible); - } - - @Override - public void insetsChanged(InsetsState insetsState) { - mOutlineManager.onInsetsChanged(insetsState); - } - - @Override - public void insetsControlChanged(InsetsState insetsState, - InsetsSourceControl[] activeControls) { - insetsChanged(insetsState); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java deleted file mode 100644 index c5d231262cd2..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreen.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.stagesplit; - -import android.annotation.IntDef; -import android.annotation.NonNull; - -import com.android.wm.shell.common.annotations.ExternalThread; -import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; - -import java.util.concurrent.Executor; - -/** - * Interface to engage split-screen feature. - * TODO: Figure out which of these are actually needed outside of the Shell - */ -@ExternalThread -public interface SplitScreen { - /** - * Stage type isn't specified normally meaning to use what ever the default is. - * E.g. exit split-screen and launch the app in fullscreen. - */ - int STAGE_TYPE_UNDEFINED = -1; - /** - * The main stage type. - * @see MainStage - */ - int STAGE_TYPE_MAIN = 0; - - /** - * The side stage type. - * @see SideStage - */ - int STAGE_TYPE_SIDE = 1; - - @IntDef(prefix = { "STAGE_TYPE_" }, value = { - STAGE_TYPE_UNDEFINED, - STAGE_TYPE_MAIN, - STAGE_TYPE_SIDE - }) - @interface StageType {} - - /** Callback interface for listening to changes in a split-screen stage. */ - interface SplitScreenListener { - default void onStagePositionChanged(@StageType int stage, @SplitPosition int position) {} - default void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {} - default void onSplitVisibilityChanged(boolean visible) {} - } - - /** Registers listener that gets split screen callback. */ - void registerSplitScreenListener(@NonNull SplitScreenListener listener, - @NonNull Executor executor); - - /** Unregisters listener that gets split screen callback. */ - void unregisterSplitScreenListener(@NonNull SplitScreenListener listener); - - /** - * Returns a binder that can be passed to an external process to manipulate SplitScreen. - */ - default ISplitScreen createExternalInterface() { - return null; - } - - /** - * Called when the keyguard occluded state changes. - * @param occluded Indicates if the keyguard is now occluded. - */ - void onKeyguardOccludedChanged(boolean occluded); - - /** - * Called when the visibility of the keyguard changes. - * @param showing Indicates if the keyguard is now visible. - */ - void onKeyguardVisibilityChanged(boolean showing); - - /** Get a string representation of a stage type */ - static String stageTypeToString(@StageType int stage) { - switch (stage) { - case STAGE_TYPE_UNDEFINED: return "UNDEFINED"; - case STAGE_TYPE_MAIN: return "MAIN"; - case STAGE_TYPE_SIDE: return "SIDE"; - default: return "UNKNOWN(" + stage + ")"; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java deleted file mode 100644 index 07174051a344..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenController.java +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.stagesplit; - -import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.RemoteAnimationTarget.MODE_OPENING; - -import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; - -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.app.PendingIntent; -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.pm.LauncherApps; -import android.graphics.Rect; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.UserHandle; -import android.util.ArrayMap; -import android.util.Slog; -import android.view.IRemoteAnimationFinishedCallback; -import android.view.RemoteAnimationAdapter; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.view.WindowManager; -import android.window.RemoteTransition; -import android.window.WindowContainerTransaction; - -import androidx.annotation.BinderThread; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.internal.logging.InstanceId; -import com.android.internal.util.FrameworkStatsLog; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.RemoteCallable; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.common.annotations.ExternalThread; -import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; -import com.android.wm.shell.draganddrop.DragAndDropPolicy; -import com.android.wm.shell.transition.LegacyTransitions; -import com.android.wm.shell.transition.Transitions; - -import java.io.PrintWriter; -import java.util.Arrays; -import java.util.Optional; -import java.util.concurrent.Executor; - -import javax.inject.Provider; - -/** - * Class manages split-screen multitasking mode and implements the main interface - * {@link SplitScreen}. - * @see StageCoordinator - */ -// TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen. -public class SplitScreenController implements DragAndDropPolicy.Starter, - RemoteCallable<SplitScreenController> { - private static final String TAG = SplitScreenController.class.getSimpleName(); - - private final ShellTaskOrganizer mTaskOrganizer; - private final SyncTransactionQueue mSyncQueue; - private final Context mContext; - private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; - private final ShellExecutor mMainExecutor; - private final SplitScreenImpl mImpl = new SplitScreenImpl(); - private final DisplayImeController mDisplayImeController; - private final DisplayInsetsController mDisplayInsetsController; - private final Transitions mTransitions; - private final TransactionPool mTransactionPool; - private final SplitscreenEventLogger mLogger; - private final Provider<Optional<StageTaskUnfoldController>> mUnfoldControllerProvider; - - private StageCoordinator mStageCoordinator; - - public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer, - SyncTransactionQueue syncQueue, Context context, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, - ShellExecutor mainExecutor, DisplayImeController displayImeController, - DisplayInsetsController displayInsetsController, - Transitions transitions, TransactionPool transactionPool, - Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { - mTaskOrganizer = shellTaskOrganizer; - mSyncQueue = syncQueue; - mContext = context; - mRootTDAOrganizer = rootTDAOrganizer; - mMainExecutor = mainExecutor; - mDisplayImeController = displayImeController; - mDisplayInsetsController = displayInsetsController; - mTransitions = transitions; - mTransactionPool = transactionPool; - mUnfoldControllerProvider = unfoldControllerProvider; - mLogger = new SplitscreenEventLogger(); - } - - public SplitScreen asSplitScreen() { - return mImpl; - } - - @Override - public Context getContext() { - return mContext; - } - - @Override - public ShellExecutor getRemoteCallExecutor() { - return mMainExecutor; - } - - public void onOrganizerRegistered() { - if (mStageCoordinator == null) { - // TODO: Multi-display - mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, - mRootTDAOrganizer, mTaskOrganizer, mDisplayImeController, - mDisplayInsetsController, mTransitions, mTransactionPool, mLogger, - mUnfoldControllerProvider); - } - } - - public boolean isSplitScreenVisible() { - return mStageCoordinator.isSplitScreenVisible(); - } - - public boolean moveToSideStage(int taskId, @SplitPosition int sideStagePosition) { - final ActivityManager.RunningTaskInfo task = mTaskOrganizer.getRunningTaskInfo(taskId); - if (task == null) { - throw new IllegalArgumentException("Unknown taskId" + taskId); - } - return moveToSideStage(task, sideStagePosition); - } - - public boolean moveToSideStage(ActivityManager.RunningTaskInfo task, - @SplitPosition int sideStagePosition) { - return mStageCoordinator.moveToSideStage(task, sideStagePosition); - } - - public boolean removeFromSideStage(int taskId) { - return mStageCoordinator.removeFromSideStage(taskId); - } - - public void setSideStageOutline(boolean enable) { - mStageCoordinator.setSideStageOutline(enable); - } - - public void setSideStagePosition(@SplitPosition int sideStagePosition) { - mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */); - } - - public void setSideStageVisibility(boolean visible) { - mStageCoordinator.setSideStageVisibility(visible); - } - - public void enterSplitScreen(int taskId, boolean leftOrTop) { - moveToSideStage(taskId, - leftOrTop ? SPLIT_POSITION_TOP_OR_LEFT : SPLIT_POSITION_BOTTOM_OR_RIGHT); - } - - public void exitSplitScreen(int toTopTaskId, int exitReason) { - mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason); - } - - public void onKeyguardOccludedChanged(boolean occluded) { - mStageCoordinator.onKeyguardOccludedChanged(occluded); - } - - public void onKeyguardVisibilityChanged(boolean showing) { - mStageCoordinator.onKeyguardVisibilityChanged(showing); - } - - public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { - mStageCoordinator.exitSplitScreenOnHide(exitSplitScreenOnHide); - } - - public void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { - mStageCoordinator.getStageBounds(outTopOrLeftBounds, outBottomOrRightBounds); - } - - public void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) { - mStageCoordinator.registerSplitScreenListener(listener); - } - - public void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) { - mStageCoordinator.unregisterSplitScreenListener(listener); - } - - public void startTask(int taskId, @SplitPosition int position, @Nullable Bundle options) { - options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, - null /* wct */); - - try { - ActivityTaskManager.getService().startActivityFromRecents(taskId, options); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to launch task", e); - } - } - - public void startShortcut(String packageName, String shortcutId, @SplitPosition int position, - @Nullable Bundle options, UserHandle user) { - options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, - null /* wct */); - - try { - LauncherApps launcherApps = - mContext.getSystemService(LauncherApps.class); - launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */, - options, user); - } catch (ActivityNotFoundException e) { - Slog.e(TAG, "Failed to launch shortcut", e); - } - } - - public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position, - @Nullable Bundle options) { - if (!Transitions.ENABLE_SHELL_TRANSITIONS) { - startIntentLegacy(intent, fillInIntent, position, options); - return; - } - mStageCoordinator.startIntent(intent, fillInIntent, STAGE_TYPE_UNDEFINED, position, options, - null /* remote */); - } - - private void startIntentLegacy(PendingIntent intent, Intent fillInIntent, - @SplitPosition int position, @Nullable Bundle options) { - LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() { - @Override - public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, - IRemoteAnimationFinishedCallback finishedCallback, - SurfaceControl.Transaction t) { - mStageCoordinator.updateSurfaceBounds(null /* layout */, t, - false /* applyResizingOffset */); - - if (apps != null) { - for (int i = 0; i < apps.length; ++i) { - if (apps[i].mode == MODE_OPENING) { - t.show(apps[i].leash); - } - } - } - - t.apply(); - if (finishedCallback != null) { - try { - finishedCallback.onAnimationFinished(); - } catch (RemoteException e) { - Slog.e(TAG, "Error finishing legacy transition: ", e); - } - } - } - }; - WindowContainerTransaction wct = new WindowContainerTransaction(); - options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct); - wct.sendPendingIntent(intent, fillInIntent, options); - mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct); - } - - RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, RemoteAnimationTarget[] apps) { - if (!isSplitScreenVisible()) return null; - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) - .setContainerLayer() - .setName("RecentsAnimationSplitTasks") - .setHidden(false) - .setCallsite("SplitScreenController#onGoingtoRecentsLegacy"); - mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder); - SurfaceControl sc = builder.build(); - SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); - - // Ensure that we order these in the parent in the right z-order as their previous order - Arrays.sort(apps, (a1, a2) -> a1.prefixOrderIndex - a2.prefixOrderIndex); - int layer = 1; - for (RemoteAnimationTarget appTarget : apps) { - transaction.reparent(appTarget.leash, sc); - transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left, - appTarget.screenSpaceBounds.top); - transaction.setLayer(appTarget.leash, layer++); - } - transaction.apply(); - transaction.close(); - return new RemoteAnimationTarget[]{ - mStageCoordinator.getDividerBarLegacyTarget(), - mStageCoordinator.getOutlineLegacyTarget()}; - } - - /** - * Sets drag info to be logged when splitscreen is entered. - */ - public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) { - mStageCoordinator.logOnDroppedToSplit(position, dragSessionId); - } - - public void dump(@NonNull PrintWriter pw, String prefix) { - pw.println(prefix + TAG); - if (mStageCoordinator != null) { - mStageCoordinator.dump(pw, prefix); - } - } - - /** - * The interface for calls from outside the Shell, within the host process. - */ - @ExternalThread - private class SplitScreenImpl implements SplitScreen { - private ISplitScreenImpl mISplitScreen; - private final ArrayMap<SplitScreenListener, Executor> mExecutors = new ArrayMap<>(); - private final SplitScreenListener mListener = new SplitScreenListener() { - @Override - public void onStagePositionChanged(int stage, int position) { - for (int i = 0; i < mExecutors.size(); i++) { - final int index = i; - mExecutors.valueAt(index).execute(() -> { - mExecutors.keyAt(index).onStagePositionChanged(stage, position); - }); - } - } - - @Override - public void onTaskStageChanged(int taskId, int stage, boolean visible) { - for (int i = 0; i < mExecutors.size(); i++) { - final int index = i; - mExecutors.valueAt(index).execute(() -> { - mExecutors.keyAt(index).onTaskStageChanged(taskId, stage, visible); - }); - } - } - - @Override - public void onSplitVisibilityChanged(boolean visible) { - for (int i = 0; i < mExecutors.size(); i++) { - final int index = i; - mExecutors.valueAt(index).execute(() -> { - mExecutors.keyAt(index).onSplitVisibilityChanged(visible); - }); - } - } - }; - - @Override - public ISplitScreen createExternalInterface() { - if (mISplitScreen != null) { - mISplitScreen.invalidate(); - } - mISplitScreen = new ISplitScreenImpl(SplitScreenController.this); - return mISplitScreen; - } - - @Override - public void onKeyguardOccludedChanged(boolean occluded) { - mMainExecutor.execute(() -> { - SplitScreenController.this.onKeyguardOccludedChanged(occluded); - }); - } - - @Override - public void registerSplitScreenListener(SplitScreenListener listener, Executor executor) { - if (mExecutors.containsKey(listener)) return; - - mMainExecutor.execute(() -> { - if (mExecutors.size() == 0) { - SplitScreenController.this.registerSplitScreenListener(mListener); - } - - mExecutors.put(listener, executor); - }); - - executor.execute(() -> { - mStageCoordinator.sendStatusToListener(listener); - }); - } - - @Override - public void unregisterSplitScreenListener(SplitScreenListener listener) { - mMainExecutor.execute(() -> { - mExecutors.remove(listener); - - if (mExecutors.size() == 0) { - SplitScreenController.this.unregisterSplitScreenListener(mListener); - } - }); - } - - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - mMainExecutor.execute(() -> { - SplitScreenController.this.onKeyguardVisibilityChanged(showing); - }); - } - } - - /** - * The interface for calls from outside the host process. - */ - @BinderThread - private static class ISplitScreenImpl extends ISplitScreen.Stub { - private SplitScreenController mController; - private ISplitScreenListener mListener; - private final SplitScreen.SplitScreenListener mSplitScreenListener = - new SplitScreen.SplitScreenListener() { - @Override - public void onStagePositionChanged(int stage, int position) { - try { - if (mListener != null) { - mListener.onStagePositionChanged(stage, position); - } - } catch (RemoteException e) { - Slog.e(TAG, "onStagePositionChanged", e); - } - } - - @Override - public void onTaskStageChanged(int taskId, int stage, boolean visible) { - try { - if (mListener != null) { - mListener.onTaskStageChanged(taskId, stage, visible); - } - } catch (RemoteException e) { - Slog.e(TAG, "onTaskStageChanged", e); - } - } - }; - private final IBinder.DeathRecipient mListenerDeathRecipient = - new IBinder.DeathRecipient() { - @Override - @BinderThread - public void binderDied() { - final SplitScreenController controller = mController; - controller.getRemoteCallExecutor().execute(() -> { - mListener = null; - controller.unregisterSplitScreenListener(mSplitScreenListener); - }); - } - }; - - public ISplitScreenImpl(SplitScreenController controller) { - mController = controller; - } - - /** - * Invalidates this instance, preventing future calls from updating the controller. - */ - void invalidate() { - mController = null; - } - - @Override - public void registerSplitScreenListener(ISplitScreenListener listener) { - executeRemoteCallWithTaskPermission(mController, "registerSplitScreenListener", - (controller) -> { - if (mListener != null) { - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - if (listener != null) { - try { - listener.asBinder().linkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } catch (RemoteException e) { - Slog.e(TAG, "Failed to link to death"); - return; - } - } - mListener = listener; - controller.registerSplitScreenListener(mSplitScreenListener); - }); - } - - @Override - public void unregisterSplitScreenListener(ISplitScreenListener listener) { - executeRemoteCallWithTaskPermission(mController, "unregisterSplitScreenListener", - (controller) -> { - if (mListener != null) { - mListener.asBinder().unlinkToDeath(mListenerDeathRecipient, - 0 /* flags */); - } - mListener = null; - controller.unregisterSplitScreenListener(mSplitScreenListener); - }); - } - - @Override - public void exitSplitScreen(int toTopTaskId) { - executeRemoteCallWithTaskPermission(mController, "exitSplitScreen", - (controller) -> { - controller.exitSplitScreen(toTopTaskId, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT); - }); - } - - @Override - public void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { - executeRemoteCallWithTaskPermission(mController, "exitSplitScreenOnHide", - (controller) -> { - controller.exitSplitScreenOnHide(exitSplitScreenOnHide); - }); - } - - @Override - public void setSideStageVisibility(boolean visible) { - executeRemoteCallWithTaskPermission(mController, "setSideStageVisibility", - (controller) -> { - controller.setSideStageVisibility(visible); - }); - } - - @Override - public void removeFromSideStage(int taskId) { - executeRemoteCallWithTaskPermission(mController, "removeFromSideStage", - (controller) -> { - controller.removeFromSideStage(taskId); - }); - } - - @Override - public void startTask(int taskId, int stage, int position, @Nullable Bundle options) { - executeRemoteCallWithTaskPermission(mController, "startTask", - (controller) -> { - controller.startTask(taskId, position, options); - }); - } - - @Override - public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions, - int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, - RemoteAnimationAdapter adapter) { - executeRemoteCallWithTaskPermission(mController, "startTasks", - (controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition( - mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition, - adapter)); - } - - @Override - public void startTasks(int mainTaskId, @Nullable Bundle mainOptions, - int sideTaskId, @Nullable Bundle sideOptions, - @SplitPosition int sidePosition, - @Nullable RemoteTransition remoteTransition) { - executeRemoteCallWithTaskPermission(mController, "startTasks", - (controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions, - sideTaskId, sideOptions, sidePosition, remoteTransition)); - } - - @Override - public void startShortcut(String packageName, String shortcutId, int stage, int position, - @Nullable Bundle options, UserHandle user) { - executeRemoteCallWithTaskPermission(mController, "startShortcut", - (controller) -> { - controller.startShortcut(packageName, shortcutId, position, - options, user); - }); - } - - @Override - public void startIntent(PendingIntent intent, Intent fillInIntent, int stage, int position, - @Nullable Bundle options) { - executeRemoteCallWithTaskPermission(mController, "startIntent", - (controller) -> { - controller.startIntent(intent, fillInIntent, position, options); - }); - } - - @Override - public RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel, - RemoteAnimationTarget[] apps) { - final RemoteAnimationTarget[][] out = new RemoteAnimationTarget[][]{null}; - executeRemoteCallWithTaskPermission(mController, "onGoingToRecentsLegacy", - (controller) -> out[0] = controller.onGoingToRecentsLegacy(cancel, apps), - true /* blocking */); - return out[0]; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java deleted file mode 100644 index 018365420177..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.stagesplit; - -import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_CLOSE; -import static android.view.WindowManager.TRANSIT_OPEN; -import static android.view.WindowManager.TRANSIT_TO_BACK; -import static android.view.WindowManager.TRANSIT_TO_FRONT; -import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM; - -import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP; -import static com.android.wm.shell.transition.Transitions.isOpeningType; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.graphics.Rect; -import android.os.IBinder; -import android.view.SurfaceControl; -import android.view.WindowManager; -import android.window.RemoteTransition; -import android.window.TransitionInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.transition.OneShotRemoteHandler; -import com.android.wm.shell.transition.Transitions; - -import java.util.ArrayList; - -/** Manages transition animations for split-screen. */ -class SplitScreenTransitions { - private static final String TAG = "SplitScreenTransitions"; - - /** Flag applied to a transition change to identify it as a divider bar for animation. */ - public static final int FLAG_IS_DIVIDER_BAR = FLAG_FIRST_CUSTOM; - - private final TransactionPool mTransactionPool; - private final Transitions mTransitions; - private final Runnable mOnFinish; - - IBinder mPendingDismiss = null; - IBinder mPendingEnter = null; - - private IBinder mAnimatingTransition = null; - private OneShotRemoteHandler mRemoteHandler = null; - - private Transitions.TransitionFinishCallback mRemoteFinishCB = (wct, wctCB) -> { - if (wct != null || wctCB != null) { - throw new UnsupportedOperationException("finish transactions not supported yet."); - } - onFinish(); - }; - - /** Keeps track of currently running animations */ - private final ArrayList<Animator> mAnimations = new ArrayList<>(); - - private Transitions.TransitionFinishCallback mFinishCallback = null; - private SurfaceControl.Transaction mFinishTransaction; - - SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions, - @NonNull Runnable onFinishCallback) { - mTransactionPool = pool; - mTransitions = transitions; - mOnFinish = onFinishCallback; - } - - void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback, - @NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot) { - mFinishCallback = finishCallback; - mAnimatingTransition = transition; - if (mRemoteHandler != null) { - mRemoteHandler.startAnimation(transition, info, startTransaction, finishTransaction, - mRemoteFinishCB); - mRemoteHandler = null; - return; - } - playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot); - } - - private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot, - @NonNull WindowContainerToken sideRoot) { - mFinishTransaction = mTransactionPool.acquire(); - - // Play some place-holder fade animations - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final SurfaceControl leash = change.getLeash(); - final int mode = info.getChanges().get(i).getMode(); - - if (mode == TRANSIT_CHANGE) { - if (change.getParent() != null) { - // This is probably reparented, so we want the parent to be immediately visible - final TransitionInfo.Change parentChange = info.getChange(change.getParent()); - t.show(parentChange.getLeash()); - t.setAlpha(parentChange.getLeash(), 1.f); - // and then animate this layer outside the parent (since, for example, this is - // the home task animating from fullscreen to part-screen). - t.reparent(leash, info.getRootLeash()); - t.setLayer(leash, info.getChanges().size() - i); - // build the finish reparent/reposition - mFinishTransaction.reparent(leash, parentChange.getLeash()); - mFinishTransaction.setPosition(leash, - change.getEndRelOffset().x, change.getEndRelOffset().y); - } - // TODO(shell-transitions): screenshot here - final Rect startBounds = new Rect(change.getStartAbsBounds()); - final Rect endBounds = new Rect(change.getEndAbsBounds()); - startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y); - endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y); - startExampleResizeAnimation(leash, startBounds, endBounds); - } - if (change.getParent() != null) { - continue; - } - - if (transition == mPendingEnter && (mainRoot.equals(change.getContainer()) - || sideRoot.equals(change.getContainer()))) { - t.setWindowCrop(leash, change.getStartAbsBounds().width(), - change.getStartAbsBounds().height()); - } - boolean isOpening = isOpeningType(info.getType()); - if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { - // fade in - startExampleAnimation(leash, true /* show */); - } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { - // fade out - if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) { - // Dismissing via snap-to-top/bottom means that the dismissed task is already - // not-visible (usually cropped to oblivion) so immediately set its alpha to 0 - // and don't animate it so it doesn't pop-in when reparented. - t.setAlpha(leash, 0.f); - } else { - startExampleAnimation(leash, false /* show */); - } - } - } - t.apply(); - onFinish(); - } - - /** Starts a transition to enter split with a remote transition animator. */ - IBinder startEnterTransition(@WindowManager.TransitionType int transitType, - @NonNull WindowContainerTransaction wct, @Nullable RemoteTransition remoteTransition, - @NonNull Transitions.TransitionHandler handler) { - if (remoteTransition != null) { - // Wrapping it for ease-of-use (OneShot handles all the binder linking/death stuff) - mRemoteHandler = new OneShotRemoteHandler( - mTransitions.getMainExecutor(), remoteTransition); - } - final IBinder transition = mTransitions.startTransition(transitType, wct, handler); - mPendingEnter = transition; - if (mRemoteHandler != null) { - mRemoteHandler.setTransition(transition); - } - return transition; - } - - /** Starts a transition for dismissing split after dragging the divider to a screen edge */ - IBinder startSnapToDismiss(@NonNull WindowContainerTransaction wct, - @NonNull Transitions.TransitionHandler handler) { - final IBinder transition = mTransitions.startTransition( - TRANSIT_SPLIT_DISMISS_SNAP, wct, handler); - mPendingDismiss = transition; - return transition; - } - - void onFinish() { - if (!mAnimations.isEmpty()) return; - mOnFinish.run(); - if (mFinishTransaction != null) { - mFinishTransaction.apply(); - mTransactionPool.release(mFinishTransaction); - mFinishTransaction = null; - } - mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); - mFinishCallback = null; - if (mAnimatingTransition == mPendingEnter) { - mPendingEnter = null; - } - if (mAnimatingTransition == mPendingDismiss) { - mPendingDismiss = null; - } - mAnimatingTransition = null; - } - - // TODO(shell-transitions): real animations - private void startExampleAnimation(@NonNull SurfaceControl leash, boolean show) { - final float end = show ? 1.f : 0.f; - final float start = 1.f - end; - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final ValueAnimator va = ValueAnimator.ofFloat(start, end); - va.setDuration(500); - va.addUpdateListener(animation -> { - float fraction = animation.getAnimatedFraction(); - transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); - transaction.apply(); - }); - final Runnable finisher = () -> { - transaction.setAlpha(leash, end); - transaction.apply(); - mTransactionPool.release(transaction); - mTransitions.getMainExecutor().execute(() -> { - mAnimations.remove(va); - onFinish(); - }); - }; - va.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { } - - @Override - public void onAnimationEnd(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationCancel(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationRepeat(Animator animation) { } - }); - mAnimations.add(va); - mTransitions.getAnimExecutor().execute(va::start); - } - - // TODO(shell-transitions): real animations - private void startExampleResizeAnimation(@NonNull SurfaceControl leash, - @NonNull Rect startBounds, @NonNull Rect endBounds) { - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - final ValueAnimator va = ValueAnimator.ofFloat(0.f, 1.f); - va.setDuration(500); - va.addUpdateListener(animation -> { - float fraction = animation.getAnimatedFraction(); - transaction.setWindowCrop(leash, - (int) (startBounds.width() * (1.f - fraction) + endBounds.width() * fraction), - (int) (startBounds.height() * (1.f - fraction) - + endBounds.height() * fraction)); - transaction.setPosition(leash, - startBounds.left * (1.f - fraction) + endBounds.left * fraction, - startBounds.top * (1.f - fraction) + endBounds.top * fraction); - transaction.apply(); - }); - final Runnable finisher = () -> { - transaction.setWindowCrop(leash, 0, 0); - transaction.setPosition(leash, endBounds.left, endBounds.top); - transaction.apply(); - mTransactionPool.release(transaction); - mTransitions.getMainExecutor().execute(() -> { - mAnimations.remove(va); - onFinish(); - }); - }; - va.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - finisher.run(); - } - - @Override - public void onAnimationCancel(Animator animation) { - finisher.run(); - } - }); - mAnimations.add(va); - mTransitions.getAnimExecutor().execute(va::start); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java deleted file mode 100644 index e1850396a5c0..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitscreenEventLogger.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.stagesplit; - -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; - -import com.android.internal.logging.InstanceId; -import com.android.internal.logging.InstanceIdSequence; -import com.android.internal.util.FrameworkStatsLog; -import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; - -/** - * Helper class that to log Drag & Drop UIEvents for a single session, see also go/uievent - */ -public class SplitscreenEventLogger { - - // Used to generate instance ids for this drag if one is not provided - private final InstanceIdSequence mIdSequence; - - // The instance id for the current splitscreen session (from start to end) - private InstanceId mLoggerSessionId; - - // Drag info - private @SplitPosition int mDragEnterPosition; - private InstanceId mDragEnterSessionId; - - // For deduping async events - private int mLastMainStagePosition = -1; - private int mLastMainStageUid = -1; - private int mLastSideStagePosition = -1; - private int mLastSideStageUid = -1; - private float mLastSplitRatio = -1f; - - public SplitscreenEventLogger() { - mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE); - } - - /** - * Return whether a splitscreen session has started. - */ - public boolean hasStartedSession() { - return mLoggerSessionId != null; - } - - /** - * May be called before logEnter() to indicate that the session was started from a drag. - */ - public void enterRequestedByDrag(@SplitPosition int position, InstanceId dragSessionId) { - mDragEnterPosition = position; - mDragEnterSessionId = dragSessionId; - } - - /** - * Logs when the user enters splitscreen. - */ - public void logEnter(float splitRatio, - @SplitPosition int mainStagePosition, int mainStageUid, - @SplitPosition int sideStagePosition, int sideStageUid, - boolean isLandscape) { - mLoggerSessionId = mIdSequence.newInstanceId(); - int enterReason = mDragEnterPosition != SPLIT_POSITION_UNDEFINED - ? getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape) - : SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW; - updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), - mainStageUid); - updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), - sideStageUid); - updateSplitRatioState(splitRatio); - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__ENTER, - enterReason, - 0 /* exitReason */, - splitRatio, - mLastMainStagePosition, - mLastMainStageUid, - mLastSideStagePosition, - mLastSideStageUid, - mDragEnterSessionId != null ? mDragEnterSessionId.getId() : 0, - mLoggerSessionId.getId()); - } - - /** - * Logs when the user exits splitscreen. Only one of the main or side stages should be - * specified to indicate which position was focused as a part of exiting (both can be unset). - */ - public void logExit(int exitReason, @SplitPosition int mainStagePosition, int mainStageUid, - @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) { - if (mLoggerSessionId == null) { - // Ignore changes until we've started logging the session - return; - } - if ((mainStagePosition != SPLIT_POSITION_UNDEFINED - && sideStagePosition != SPLIT_POSITION_UNDEFINED) - || (mainStageUid != 0 && sideStageUid != 0)) { - throw new IllegalArgumentException("Only main or side stage should be set"); - } - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__EXIT, - 0 /* enterReason */, - exitReason, - 0f /* splitRatio */, - getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), - mainStageUid, - getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), - sideStageUid, - 0 /* dragInstanceId */, - mLoggerSessionId.getId()); - - // Reset states - mLoggerSessionId = null; - mDragEnterPosition = SPLIT_POSITION_UNDEFINED; - mDragEnterSessionId = null; - mLastMainStagePosition = -1; - mLastMainStageUid = -1; - mLastSideStagePosition = -1; - mLastSideStageUid = -1; - } - - /** - * Logs when an app in the main stage changes. - */ - public void logMainStageAppChange(@SplitPosition int mainStagePosition, int mainStageUid, - boolean isLandscape) { - if (mLoggerSessionId == null) { - // Ignore changes until we've started logging the session - return; - } - if (!updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, - isLandscape), mainStageUid)) { - // Ignore if there are no user perceived changes - return; - } - - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE, - 0 /* enterReason */, - 0 /* exitReason */, - 0f /* splitRatio */, - mLastMainStagePosition, - mLastMainStageUid, - 0 /* sideStagePosition */, - 0 /* sideStageUid */, - 0 /* dragInstanceId */, - mLoggerSessionId.getId()); - } - - /** - * Logs when an app in the side stage changes. - */ - public void logSideStageAppChange(@SplitPosition int sideStagePosition, int sideStageUid, - boolean isLandscape) { - if (mLoggerSessionId == null) { - // Ignore changes until we've started logging the session - return; - } - if (!updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, - isLandscape), sideStageUid)) { - // Ignore if there are no user perceived changes - return; - } - - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__APP_CHANGE, - 0 /* enterReason */, - 0 /* exitReason */, - 0f /* splitRatio */, - 0 /* mainStagePosition */, - 0 /* mainStageUid */, - mLastSideStagePosition, - mLastSideStageUid, - 0 /* dragInstanceId */, - mLoggerSessionId.getId()); - } - - /** - * Logs when the splitscreen ratio changes. - */ - public void logResize(float splitRatio) { - if (mLoggerSessionId == null) { - // Ignore changes until we've started logging the session - return; - } - if (splitRatio <= 0f || splitRatio >= 1f) { - // Don't bother reporting resizes that end up dismissing the split, that will be logged - // via the exit event - return; - } - if (!updateSplitRatioState(splitRatio)) { - // Ignore if there are no user perceived changes - return; - } - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__RESIZE, - 0 /* enterReason */, - 0 /* exitReason */, - mLastSplitRatio, - 0 /* mainStagePosition */, 0 /* mainStageUid */, - 0 /* sideStagePosition */, 0 /* sideStageUid */, - 0 /* dragInstanceId */, - mLoggerSessionId.getId()); - } - - /** - * Logs when the apps in splitscreen are swapped. - */ - public void logSwap(@SplitPosition int mainStagePosition, int mainStageUid, - @SplitPosition int sideStagePosition, int sideStageUid, boolean isLandscape) { - if (mLoggerSessionId == null) { - // Ignore changes until we've started logging the session - return; - } - - updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape), - mainStageUid); - updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape), - sideStageUid); - FrameworkStatsLog.write(FrameworkStatsLog.SPLITSCREEN_UI_CHANGED, - FrameworkStatsLog.SPLITSCREEN_UICHANGED__ACTION__SWAP, - 0 /* enterReason */, - 0 /* exitReason */, - 0f /* splitRatio */, - mLastMainStagePosition, - mLastMainStageUid, - mLastSideStagePosition, - mLastSideStageUid, - 0 /* dragInstanceId */, - mLoggerSessionId.getId()); - } - - private boolean updateMainStageState(int mainStagePosition, int mainStageUid) { - boolean changed = (mLastMainStagePosition != mainStagePosition) - || (mLastMainStageUid != mainStageUid); - if (!changed) { - return false; - } - - mLastMainStagePosition = mainStagePosition; - mLastMainStageUid = mainStageUid; - return true; - } - - private boolean updateSideStageState(int sideStagePosition, int sideStageUid) { - boolean changed = (mLastSideStagePosition != sideStagePosition) - || (mLastSideStageUid != sideStageUid); - if (!changed) { - return false; - } - - mLastSideStagePosition = sideStagePosition; - mLastSideStageUid = sideStageUid; - return true; - } - - private boolean updateSplitRatioState(float splitRatio) { - boolean changed = Float.compare(mLastSplitRatio, splitRatio) != 0; - if (!changed) { - return false; - } - - mLastSplitRatio = splitRatio; - return true; - } - - public int getDragEnterReasonFromSplitPosition(@SplitPosition int position, - boolean isLandscape) { - if (isLandscape) { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_LEFT - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_RIGHT; - } else { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_TOP - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__DRAG_BOTTOM; - } - } - - private int getMainStagePositionFromSplitPosition(@SplitPosition int position, - boolean isLandscape) { - if (position == SPLIT_POSITION_UNDEFINED) { - return 0; - } - if (isLandscape) { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__LEFT - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__RIGHT; - } else { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__TOP - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__MAIN_STAGE_POSITION__BOTTOM; - } - } - - private int getSideStagePositionFromSplitPosition(@SplitPosition int position, - boolean isLandscape) { - if (position == SPLIT_POSITION_UNDEFINED) { - return 0; - } - if (isLandscape) { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__LEFT - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__RIGHT; - } else { - return position == SPLIT_POSITION_TOP_OR_LEFT - ? FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__TOP - : FrameworkStatsLog.SPLITSCREEN_UICHANGED__SIDE_STAGE_POSITION__BOTTOM; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java deleted file mode 100644 index ac25c7510931..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageCoordinator.java +++ /dev/null @@ -1,1333 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.stagesplit; - -import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; -import static android.view.WindowManager.TRANSIT_OPEN; -import static android.view.WindowManager.TRANSIT_TO_BACK; -import static android.view.WindowManager.TRANSIT_TO_FRONT; -import static android.view.WindowManager.transitTypeToString; - -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW; -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED; -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED; -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER; -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME; -import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; -import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_MAIN; -import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_SIDE; -import static com.android.wm.shell.stagesplit.SplitScreen.STAGE_TYPE_UNDEFINED; -import static com.android.wm.shell.stagesplit.SplitScreen.stageTypeToString; -import static com.android.wm.shell.stagesplit.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR; -import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; -import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP; -import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; -import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; -import static com.android.wm.shell.transition.Transitions.isClosingType; -import static com.android.wm.shell.transition.Transitions.isOpeningType; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.ActivityOptions; -import android.app.ActivityTaskManager; -import android.app.PendingIntent; -import android.app.WindowConfiguration; -import android.content.Context; -import android.content.Intent; -import android.graphics.Rect; -import android.hardware.devicestate.DeviceStateManager; -import android.os.Bundle; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; -import android.util.Slog; -import android.view.IRemoteAnimationFinishedCallback; -import android.view.IRemoteAnimationRunner; -import android.view.RemoteAnimationAdapter; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.view.WindowManager; -import android.window.DisplayAreaInfo; -import android.window.RemoteTransition; -import android.window.TransitionInfo; -import android.window.TransitionRequestInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.logging.InstanceId; -import com.android.internal.protolog.common.ProtoLog; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.SyncTransactionQueue; -import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.common.split.SplitLayout; -import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; -import com.android.wm.shell.common.split.SplitWindowManager; -import com.android.wm.shell.protolog.ShellProtoLogGroup; -import com.android.wm.shell.transition.Transitions; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import javax.inject.Provider; - -/** - * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and - * {@link SideStage} stages. - * Some high-level rules: - * - The {@link StageCoordinator} is only considered active if the {@link SideStage} contains at - * least one child task. - * - The {@link MainStage} should only have children if the coordinator is active. - * - The {@link SplitLayout} divider is only visible if both the {@link MainStage} - * and {@link SideStage} are visible. - * - The {@link MainStage} configuration is fullscreen when the {@link SideStage} isn't visible. - * This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and - * {@link #onStageHasChildrenChanged(StageListenerImpl).} - */ -class StageCoordinator implements SplitLayout.SplitLayoutHandler, - RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener, Transitions.TransitionHandler { - - private static final String TAG = StageCoordinator.class.getSimpleName(); - - /** internal value for mDismissTop that represents no dismiss */ - private static final int NO_DISMISS = -2; - - private final SurfaceSession mSurfaceSession = new SurfaceSession(); - - private final MainStage mMainStage; - private final StageListenerImpl mMainStageListener = new StageListenerImpl(); - private final StageTaskUnfoldController mMainUnfoldController; - private final SideStage mSideStage; - private final StageListenerImpl mSideStageListener = new StageListenerImpl(); - private final StageTaskUnfoldController mSideUnfoldController; - @SplitPosition - private int mSideStagePosition = SPLIT_POSITION_BOTTOM_OR_RIGHT; - - private final int mDisplayId; - private SplitLayout mSplitLayout; - private boolean mDividerVisible; - private final SyncTransactionQueue mSyncQueue; - private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer; - private final ShellTaskOrganizer mTaskOrganizer; - private DisplayAreaInfo mDisplayAreaInfo; - private final Context mContext; - private final List<SplitScreen.SplitScreenListener> mListeners = new ArrayList<>(); - private final DisplayImeController mDisplayImeController; - private final DisplayInsetsController mDisplayInsetsController; - private final SplitScreenTransitions mSplitTransitions; - private final SplitscreenEventLogger mLogger; - private boolean mExitSplitScreenOnHide; - private boolean mKeyguardOccluded; - - // TODO(b/187041611): remove this flag after totally deprecated legacy split - /** Whether the device is supporting legacy split or not. */ - private boolean mUseLegacySplit; - - @SplitScreen.StageType private int mDismissTop = NO_DISMISS; - - /** The target stage to dismiss to when unlock after folded. */ - @SplitScreen.StageType private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; - - private final Runnable mOnTransitionAnimationComplete = () -> { - // If still playing, let it finish. - if (!isSplitScreenVisible()) { - // Update divider state after animation so that it is still around and positioned - // properly for the animation itself. - setDividerVisibility(false); - mSplitLayout.resetDividerPosition(); - } - mDismissTop = NO_DISMISS; - }; - - private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks = - new SplitWindowManager.ParentContainerCallbacks() { - @Override - public void attachToParentSurface(SurfaceControl.Builder b) { - mRootTDAOrganizer.attachToDisplayArea(mDisplayId, b); - } - - @Override - public void onLeashReady(SurfaceControl leash) { - mSyncQueue.runInSync(t -> applyDividerVisibility(t)); - } - }; - - StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - DisplayImeController displayImeController, - DisplayInsetsController displayInsetsController, Transitions transitions, - TransactionPool transactionPool, SplitscreenEventLogger logger, - Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { - mContext = context; - mDisplayId = displayId; - mSyncQueue = syncQueue; - mRootTDAOrganizer = rootTDAOrganizer; - mTaskOrganizer = taskOrganizer; - mLogger = logger; - mMainUnfoldController = unfoldControllerProvider.get().orElse(null); - mSideUnfoldController = unfoldControllerProvider.get().orElse(null); - - mMainStage = new MainStage( - mTaskOrganizer, - mDisplayId, - mMainStageListener, - mSyncQueue, - mSurfaceSession, - mMainUnfoldController); - mSideStage = new SideStage( - mContext, - mTaskOrganizer, - mDisplayId, - mSideStageListener, - mSyncQueue, - mSurfaceSession, - mSideUnfoldController); - mDisplayImeController = displayImeController; - mDisplayInsetsController = displayInsetsController; - mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSideStage); - mRootTDAOrganizer.registerListener(displayId, this); - final DeviceStateManager deviceStateManager = - mContext.getSystemService(DeviceStateManager.class); - deviceStateManager.registerCallback(taskOrganizer.getExecutor(), - new DeviceStateManager.FoldStateListener(mContext, this::onFoldedStateChanged)); - mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, - mOnTransitionAnimationComplete); - transitions.addHandler(this); - } - - @VisibleForTesting - StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue, - RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellTaskOrganizer taskOrganizer, - MainStage mainStage, SideStage sideStage, DisplayImeController displayImeController, - DisplayInsetsController displayInsetsController, SplitLayout splitLayout, - Transitions transitions, TransactionPool transactionPool, - SplitscreenEventLogger logger, - Provider<Optional<StageTaskUnfoldController>> unfoldControllerProvider) { - mContext = context; - mDisplayId = displayId; - mSyncQueue = syncQueue; - mRootTDAOrganizer = rootTDAOrganizer; - mTaskOrganizer = taskOrganizer; - mMainStage = mainStage; - mSideStage = sideStage; - mDisplayImeController = displayImeController; - mDisplayInsetsController = displayInsetsController; - mRootTDAOrganizer.registerListener(displayId, this); - mSplitLayout = splitLayout; - mSplitTransitions = new SplitScreenTransitions(transactionPool, transitions, - mOnTransitionAnimationComplete); - mMainUnfoldController = unfoldControllerProvider.get().orElse(null); - mSideUnfoldController = unfoldControllerProvider.get().orElse(null); - mLogger = logger; - transitions.addHandler(this); - } - - @VisibleForTesting - SplitScreenTransitions getSplitTransitions() { - return mSplitTransitions; - } - - boolean isSplitScreenVisible() { - return mSideStageListener.mVisible && mMainStageListener.mVisible; - } - - boolean moveToSideStage(ActivityManager.RunningTaskInfo task, - @SplitPosition int sideStagePosition) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - setSideStagePosition(sideStagePosition, wct); - mMainStage.activate(getMainStageBounds(), wct); - mSideStage.addTask(task, getSideStageBounds(), wct); - mSyncQueue.queue(wct); - mSyncQueue.runInSync( - t -> updateSurfaceBounds(null /* layout */, t, false /* applyResizingOffset */)); - return true; - } - - boolean removeFromSideStage(int taskId) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - - /** - * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the - * {@link SideStage} no longer has children. - */ - final boolean result = mSideStage.removeTask(taskId, - mMainStage.isActive() ? mMainStage.mRootTaskInfo.token : null, - wct); - mTaskOrganizer.applyTransaction(wct); - return result; - } - - void setSideStageOutline(boolean enable) { - mSideStage.enableOutline(enable); - } - - /** Starts 2 tasks in one transition. */ - void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId, - @Nullable Bundle sideOptions, @SplitPosition int sidePosition, - @Nullable RemoteTransition remoteTransition) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - mainOptions = mainOptions != null ? mainOptions : new Bundle(); - sideOptions = sideOptions != null ? sideOptions : new Bundle(); - setSideStagePosition(sidePosition, wct); - - // Build a request WCT that will launch both apps such that task 0 is on the main stage - // while task 1 is on the side stage. - mMainStage.activate(getMainStageBounds(), wct); - mSideStage.setBounds(getSideStageBounds(), wct); - - // Make sure the launch options will put tasks in the corresponding split roots - addActivityOptions(mainOptions, mMainStage); - addActivityOptions(sideOptions, mSideStage); - - // Add task launch requests - wct.startTask(mainTaskId, mainOptions); - wct.startTask(sideTaskId, sideOptions); - - mSplitTransitions.startEnterTransition( - TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this); - } - - /** Starts 2 tasks in one legacy transition. */ - void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions, - int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition, - RemoteAnimationAdapter adapter) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Need to add another wrapper here in shell so that we can inject the divider bar - // and also manage the process elevation via setRunningRemote - IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() { - @Override - public void onAnimationStart(@WindowManager.TransitionOldType int transit, - RemoteAnimationTarget[] apps, - RemoteAnimationTarget[] wallpapers, - RemoteAnimationTarget[] nonApps, - final IRemoteAnimationFinishedCallback finishedCallback) { - RemoteAnimationTarget[] augmentedNonApps = - new RemoteAnimationTarget[nonApps.length + 1]; - for (int i = 0; i < nonApps.length; ++i) { - augmentedNonApps[i] = nonApps[i]; - } - augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget(); - try { - ActivityTaskManager.getService().setRunningRemoteTransitionDelegate( - adapter.getCallingApplication()); - adapter.getRunner().onAnimationStart(transit, apps, wallpapers, nonApps, - finishedCallback); - } catch (RemoteException e) { - Slog.e(TAG, "Error starting remote animation", e); - } - } - - @Override - public void onAnimationCancelled() { - try { - adapter.getRunner().onAnimationCancelled(); - } catch (RemoteException e) { - Slog.e(TAG, "Error starting remote animation", e); - } - } - }; - RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter( - wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay()); - - if (mainOptions == null) { - mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle(); - } else { - ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions); - mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter)); - } - - sideOptions = sideOptions != null ? sideOptions : new Bundle(); - setSideStagePosition(sidePosition, wct); - - // Build a request WCT that will launch both apps such that task 0 is on the main stage - // while task 1 is on the side stage. - mMainStage.activate(getMainStageBounds(), wct); - mSideStage.setBounds(getSideStageBounds(), wct); - - // Make sure the launch options will put tasks in the corresponding split roots - addActivityOptions(mainOptions, mMainStage); - addActivityOptions(sideOptions, mSideStage); - - // Add task launch requests - wct.startTask(mainTaskId, mainOptions); - wct.startTask(sideTaskId, sideOptions); - - // Using legacy transitions, so we can't use blast sync since it conflicts. - mTaskOrganizer.applyTransaction(wct); - } - - public void startIntent(PendingIntent intent, Intent fillInIntent, - @SplitScreen.StageType int stage, @SplitPosition int position, - @androidx.annotation.Nullable Bundle options, - @Nullable RemoteTransition remoteTransition) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - options = resolveStartStage(stage, position, options, wct); - wct.sendPendingIntent(intent, fillInIntent, options); - mSplitTransitions.startEnterTransition( - TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this); - } - - Bundle resolveStartStage(@SplitScreen.StageType int stage, - @SplitPosition int position, @androidx.annotation.Nullable Bundle options, - @androidx.annotation.Nullable WindowContainerTransaction wct) { - switch (stage) { - case STAGE_TYPE_UNDEFINED: { - // Use the stage of the specified position is valid. - if (position != SPLIT_POSITION_UNDEFINED) { - if (position == getSideStagePosition()) { - options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct); - } else { - options = resolveStartStage(STAGE_TYPE_MAIN, position, options, wct); - } - } else { - // Exit split-screen and launch fullscreen since stage wasn't specified. - prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); - } - break; - } - case STAGE_TYPE_SIDE: { - if (position != SPLIT_POSITION_UNDEFINED) { - setSideStagePosition(position, wct); - } else { - position = getSideStagePosition(); - } - if (options == null) { - options = new Bundle(); - } - updateActivityOptions(options, position); - break; - } - case STAGE_TYPE_MAIN: { - if (position != SPLIT_POSITION_UNDEFINED) { - // Set the side stage opposite of what we want to the main stage. - final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT - ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; - setSideStagePosition(sideStagePosition, wct); - } else { - position = getMainStagePosition(); - } - if (options == null) { - options = new Bundle(); - } - updateActivityOptions(options, position); - break; - } - default: - throw new IllegalArgumentException("Unknown stage=" + stage); - } - - return options; - } - - @SplitPosition - int getSideStagePosition() { - return mSideStagePosition; - } - - @SplitPosition - int getMainStagePosition() { - return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT - ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; - } - - void setSideStagePosition(@SplitPosition int sideStagePosition, - @Nullable WindowContainerTransaction wct) { - setSideStagePosition(sideStagePosition, true /* updateBounds */, wct); - } - - private void setSideStagePosition(@SplitPosition int sideStagePosition, boolean updateBounds, - @Nullable WindowContainerTransaction wct) { - if (mSideStagePosition == sideStagePosition) return; - mSideStagePosition = sideStagePosition; - sendOnStagePositionChanged(); - - if (mSideStageListener.mVisible && updateBounds) { - if (wct == null) { - // onLayoutSizeChanged builds/applies a wct with the contents of updateWindowBounds. - onLayoutSizeChanged(mSplitLayout); - } else { - updateWindowBounds(mSplitLayout, wct); - updateUnfoldBounds(); - } - } - } - - void setSideStageVisibility(boolean visible) { - if (mSideStageListener.mVisible == visible) return; - - final WindowContainerTransaction wct = new WindowContainerTransaction(); - mSideStage.setVisibility(visible, wct); - mTaskOrganizer.applyTransaction(wct); - } - - void onKeyguardOccludedChanged(boolean occluded) { - // Do not exit split directly, because it needs to wait for task info update to determine - // which task should remain on top after split dismissed. - mKeyguardOccluded = occluded; - } - - void onKeyguardVisibilityChanged(boolean showing) { - if (!showing && mMainStage.isActive() - && mTopStageAfterFoldDismiss != STAGE_TYPE_UNDEFINED) { - exitSplitScreen(mTopStageAfterFoldDismiss == STAGE_TYPE_MAIN ? mMainStage : mSideStage, - SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED); - } - } - - void exitSplitScreenOnHide(boolean exitSplitScreenOnHide) { - mExitSplitScreenOnHide = exitSplitScreenOnHide; - } - - void exitSplitScreen(int toTopTaskId, int exitReason) { - StageTaskListener childrenToTop = null; - if (mMainStage.containsTask(toTopTaskId)) { - childrenToTop = mMainStage; - } else if (mSideStage.containsTask(toTopTaskId)) { - childrenToTop = mSideStage; - } - - final WindowContainerTransaction wct = new WindowContainerTransaction(); - if (childrenToTop != null) { - childrenToTop.reorderChild(toTopTaskId, true /* onTop */, wct); - } - applyExitSplitScreen(childrenToTop, wct, exitReason); - } - - private void exitSplitScreen(StageTaskListener childrenToTop, int exitReason) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - applyExitSplitScreen(childrenToTop, wct, exitReason); - } - - private void applyExitSplitScreen( - StageTaskListener childrenToTop, - WindowContainerTransaction wct, int exitReason) { - mSideStage.removeAllTasks(wct, childrenToTop == mSideStage); - mMainStage.deactivate(wct, childrenToTop == mMainStage); - mTaskOrganizer.applyTransaction(wct); - mSyncQueue.runInSync(t -> t - .setWindowCrop(mMainStage.mRootLeash, null) - .setWindowCrop(mSideStage.mRootLeash, null)); - // Hide divider and reset its position. - setDividerVisibility(false); - mSplitLayout.resetDividerPosition(); - mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; - if (childrenToTop != null) { - logExitToStage(exitReason, childrenToTop == mMainStage); - } else { - logExit(exitReason); - } - } - - /** - * Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates - * an existing WindowContainerTransaction (rather than applying immediately). This is intended - * to be used when exiting split might be bundled with other window operations. - */ - void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop, - @NonNull WindowContainerTransaction wct) { - mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); - mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); - } - - void getStageBounds(Rect outTopOrLeftBounds, Rect outBottomOrRightBounds) { - outTopOrLeftBounds.set(mSplitLayout.getBounds1()); - outBottomOrRightBounds.set(mSplitLayout.getBounds2()); - } - - private void addActivityOptions(Bundle opts, StageTaskListener stage) { - opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token); - } - - void updateActivityOptions(Bundle opts, @SplitPosition int position) { - addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage); - } - - void registerSplitScreenListener(SplitScreen.SplitScreenListener listener) { - if (mListeners.contains(listener)) return; - mListeners.add(listener); - sendStatusToListener(listener); - } - - void unregisterSplitScreenListener(SplitScreen.SplitScreenListener listener) { - mListeners.remove(listener); - } - - void sendStatusToListener(SplitScreen.SplitScreenListener listener) { - listener.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition()); - listener.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition()); - listener.onSplitVisibilityChanged(isSplitScreenVisible()); - mSideStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_SIDE); - mMainStage.onSplitScreenListenerRegistered(listener, STAGE_TYPE_MAIN); - } - - private void sendOnStagePositionChanged() { - for (int i = mListeners.size() - 1; i >= 0; --i) { - final SplitScreen.SplitScreenListener l = mListeners.get(i); - l.onStagePositionChanged(STAGE_TYPE_MAIN, getMainStagePosition()); - l.onStagePositionChanged(STAGE_TYPE_SIDE, getSideStagePosition()); - } - } - - private void onStageChildTaskStatusChanged(StageListenerImpl stageListener, int taskId, - boolean present, boolean visible) { - int stage; - if (present) { - stage = stageListener == mSideStageListener ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; - } else { - // No longer on any stage - stage = STAGE_TYPE_UNDEFINED; - } - if (stage == STAGE_TYPE_MAIN) { - mLogger.logMainStageAppChange(getMainStagePosition(), mMainStage.getTopChildTaskUid(), - mSplitLayout.isLandscape()); - } else { - mLogger.logSideStageAppChange(getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLandscape()); - } - - for (int i = mListeners.size() - 1; i >= 0; --i) { - mListeners.get(i).onTaskStageChanged(taskId, stage, visible); - } - } - - private void sendSplitVisibilityChanged() { - for (int i = mListeners.size() - 1; i >= 0; --i) { - final SplitScreen.SplitScreenListener l = mListeners.get(i); - l.onSplitVisibilityChanged(mDividerVisible); - } - - if (mMainUnfoldController != null && mSideUnfoldController != null) { - mMainUnfoldController.onSplitVisibilityChanged(mDividerVisible); - mSideUnfoldController.onSplitVisibilityChanged(mDividerVisible); - } - } - - private void onStageRootTaskAppeared(StageListenerImpl stageListener) { - if (mMainStageListener.mHasRootTask && mSideStageListener.mHasRootTask) { - mUseLegacySplit = mContext.getResources().getBoolean(R.bool.config_useLegacySplit); - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Make the stages adjacent to each other so they occlude what's behind them. - wct.setAdjacentRoots(mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token, - true /* moveTogether */); - - // Only sets side stage as launch-adjacent-flag-root when the device is not using legacy - // split to prevent new split behavior confusing users. - if (!mUseLegacySplit) { - wct.setLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); - } - - mTaskOrganizer.applyTransaction(wct); - } - } - - private void onStageRootTaskVanished(StageListenerImpl stageListener) { - if (stageListener == mMainStageListener || stageListener == mSideStageListener) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Deactivate the main stage if it no longer has a root task. - mMainStage.deactivate(wct); - - if (!mUseLegacySplit) { - wct.clearLaunchAdjacentFlagRoot(mSideStage.mRootTaskInfo.token); - } - - mTaskOrganizer.applyTransaction(wct); - } - } - - private void setDividerVisibility(boolean visible) { - if (mDividerVisible == visible) return; - mDividerVisible = visible; - if (visible) { - mSplitLayout.init(); - updateUnfoldBounds(); - } else { - mSplitLayout.release(); - } - sendSplitVisibilityChanged(); - } - - private void onStageVisibilityChanged(StageListenerImpl stageListener) { - final boolean sideStageVisible = mSideStageListener.mVisible; - final boolean mainStageVisible = mMainStageListener.mVisible; - final boolean bothStageVisible = sideStageVisible && mainStageVisible; - final boolean bothStageInvisible = !sideStageVisible && !mainStageVisible; - final boolean sameVisibility = sideStageVisible == mainStageVisible; - // Only add or remove divider when both visible or both invisible to avoid sometimes we only - // got one stage visibility changed for a moment and it will cause flicker. - if (sameVisibility) { - setDividerVisibility(bothStageVisible); - } - - if (bothStageInvisible) { - if (mExitSplitScreenOnHide - // Don't dismiss staged split when both stages are not visible due to sleeping display, - // like the cases keyguard showing or screen off. - || (!mMainStage.mRootTaskInfo.isSleeping && !mSideStage.mRootTaskInfo.isSleeping)) { - exitSplitScreen(null /* childrenToTop */, - SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME); - } - } else if (mKeyguardOccluded) { - // At least one of the stages is visible while keyguard occluded. Dismiss split because - // there's show-when-locked activity showing on top of keyguard. Also make sure the - // task contains show-when-locked activity remains on top after split dismissed. - final StageTaskListener toTop = - mainStageVisible ? mMainStage : (sideStageVisible ? mSideStage : null); - exitSplitScreen(toTop, SPLITSCREEN_UICHANGED__EXIT_REASON__SCREEN_LOCKED_SHOW_ON_TOP); - } - - mSyncQueue.runInSync(t -> { - // Same above, we only set root tasks and divider leash visibility when both stage - // change to visible or invisible to avoid flicker. - if (sameVisibility) { - t.setVisibility(mSideStage.mRootLeash, bothStageVisible) - .setVisibility(mMainStage.mRootLeash, bothStageVisible); - applyDividerVisibility(t); - applyOutlineVisibility(t); - } - }); - } - - private void applyDividerVisibility(SurfaceControl.Transaction t) { - final SurfaceControl dividerLeash = mSplitLayout.getDividerLeash(); - if (dividerLeash == null) { - return; - } - - if (mDividerVisible) { - t.show(dividerLeash) - .setLayer(dividerLeash, Integer.MAX_VALUE) - .setPosition(dividerLeash, - mSplitLayout.getDividerBounds().left, - mSplitLayout.getDividerBounds().top); - } else { - t.hide(dividerLeash); - } - } - - private void applyOutlineVisibility(SurfaceControl.Transaction t) { - final SurfaceControl outlineLeash = mSideStage.getOutlineLeash(); - if (outlineLeash == null) { - return; - } - - if (mDividerVisible) { - t.show(outlineLeash).setLayer(outlineLeash, Integer.MAX_VALUE); - } else { - t.hide(outlineLeash); - } - } - - private void onStageHasChildrenChanged(StageListenerImpl stageListener) { - final boolean hasChildren = stageListener.mHasChildren; - final boolean isSideStage = stageListener == mSideStageListener; - if (!hasChildren) { - if (isSideStage && mMainStageListener.mVisible) { - // Exit to main stage if side stage no longer has children. - exitSplitScreen(mMainStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED); - } else if (!isSideStage && mSideStageListener.mVisible) { - // Exit to side stage if main stage no longer has children. - exitSplitScreen(mSideStage, SPLITSCREEN_UICHANGED__EXIT_REASON__APP_FINISHED); - } - } else if (isSideStage) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - // Make sure the main stage is active. - mMainStage.activate(getMainStageBounds(), wct); - mSideStage.setBounds(getSideStageBounds(), wct); - mTaskOrganizer.applyTransaction(wct); - } - if (!mLogger.hasStartedSession() && mMainStageListener.mHasChildren - && mSideStageListener.mHasChildren) { - mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(), - getMainStagePosition(), mMainStage.getTopChildTaskUid(), - getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLandscape()); - } - } - - @VisibleForTesting - IBinder onSnappedToDismissTransition(boolean mainStageToTop) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - prepareExitSplitScreen(mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE, wct); - return mSplitTransitions.startSnapToDismiss(wct, this); - } - - @Override - public void onSnappedToDismiss(boolean bottomOrRight) { - final boolean mainStageToTop = - bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT - : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; - if (ENABLE_SHELL_TRANSITIONS) { - onSnappedToDismissTransition(mainStageToTop); - return; - } - exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, - SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER); - } - - @Override - public void onDoubleTappedDivider() { - setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT - ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */); - mLogger.logSwap(getMainStagePosition(), mMainStage.getTopChildTaskUid(), - getSideStagePosition(), mSideStage.getTopChildTaskUid(), - mSplitLayout.isLandscape()); - } - - @Override - public void onLayoutPositionChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */)); - } - - @Override - public void onLayoutSizeChanging(SplitLayout layout) { - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, true /* applyResizingOffset */)); - mSideStage.setOutlineVisibility(false); - } - - @Override - public void onLayoutSizeChanged(SplitLayout layout) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - updateWindowBounds(layout, wct); - updateUnfoldBounds(); - mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t, false /* applyResizingOffset */)); - mSideStage.setOutlineVisibility(true); - mLogger.logResize(mSplitLayout.getDividerPositionAsFraction()); - } - - private void updateUnfoldBounds() { - if (mMainUnfoldController != null && mSideUnfoldController != null) { - mMainUnfoldController.onLayoutChanged(getMainStageBounds()); - mSideUnfoldController.onLayoutChanged(getSideStageBounds()); - } - } - - /** - * Populates `wct` with operations that match the split windows to the current layout. - * To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied - */ - private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { - final StageTaskListener topLeftStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; - final StageTaskListener bottomRightStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); - } - - void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, - boolean applyResizingOffset) { - final StageTaskListener topLeftStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; - final StageTaskListener bottomRightStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash, - bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer, - applyResizingOffset); - } - - @Override - public int getSplitItemPosition(WindowContainerToken token) { - if (token == null) { - return SPLIT_POSITION_UNDEFINED; - } - - if (token.equals(mMainStage.mRootTaskInfo.getToken())) { - return getMainStagePosition(); - } else if (token.equals(mSideStage.mRootTaskInfo.getToken())) { - return getSideStagePosition(); - } - - return SPLIT_POSITION_UNDEFINED; - } - - @Override - public void setLayoutOffsetTarget(int offsetX, int offsetY, SplitLayout layout) { - final StageTaskListener topLeftStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; - final StageTaskListener bottomRightStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - final WindowContainerTransaction wct = new WindowContainerTransaction(); - layout.applyLayoutOffsetTarget(wct, offsetX, offsetY, topLeftStage.mRootTaskInfo, - bottomRightStage.mRootTaskInfo); - mTaskOrganizer.applyTransaction(wct); - } - - @Override - public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) { - mDisplayAreaInfo = displayAreaInfo; - if (mSplitLayout == null) { - mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext, - mDisplayAreaInfo.configuration, this, mParentContainerCallbacks, - mDisplayImeController, mTaskOrganizer, SplitLayout.PARALLAX_DISMISSING); - mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout); - - if (mMainUnfoldController != null && mSideUnfoldController != null) { - mMainUnfoldController.init(); - mSideUnfoldController.init(); - } - } - } - - @Override - public void onDisplayAreaVanished(DisplayAreaInfo displayAreaInfo) { - throw new IllegalStateException("Well that was unexpected..."); - } - - @Override - public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) { - mDisplayAreaInfo = displayAreaInfo; - if (mSplitLayout != null - && mSplitLayout.updateConfiguration(mDisplayAreaInfo.configuration) - && mMainStage.isActive()) { - onLayoutSizeChanged(mSplitLayout); - } - } - - private void onFoldedStateChanged(boolean folded) { - mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED; - if (!folded) return; - - if (mMainStage.isFocused()) { - mTopStageAfterFoldDismiss = STAGE_TYPE_MAIN; - } else if (mSideStage.isFocused()) { - mTopStageAfterFoldDismiss = STAGE_TYPE_SIDE; - } - } - - private Rect getSideStageBounds() { - return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT - ? mSplitLayout.getBounds1() : mSplitLayout.getBounds2(); - } - - private Rect getMainStageBounds() { - return mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT - ? mSplitLayout.getBounds2() : mSplitLayout.getBounds1(); - } - - /** - * Get the stage that should contain this `taskInfo`. The stage doesn't necessarily contain - * this task (yet) so this can also be used to identify which stage to put a task into. - */ - private StageTaskListener getStageOfTask(ActivityManager.RunningTaskInfo taskInfo) { - // TODO(b/184679596): Find a way to either include task-org information in the transition, - // or synchronize task-org callbacks so we can use stage.containsTask - if (mMainStage.mRootTaskInfo != null - && taskInfo.parentTaskId == mMainStage.mRootTaskInfo.taskId) { - return mMainStage; - } else if (mSideStage.mRootTaskInfo != null - && taskInfo.parentTaskId == mSideStage.mRootTaskInfo.taskId) { - return mSideStage; - } - return null; - } - - @SplitScreen.StageType - private int getStageType(StageTaskListener stage) { - return stage == mMainStage ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; - } - - @Override - public WindowContainerTransaction handleRequest(@NonNull IBinder transition, - @Nullable TransitionRequestInfo request) { - final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask(); - if (triggerTask == null) { - // still want to monitor everything while in split-screen, so return non-null. - return isSplitScreenVisible() ? new WindowContainerTransaction() : null; - } - - WindowContainerTransaction out = null; - final @WindowManager.TransitionType int type = request.getType(); - if (isSplitScreenVisible()) { - // try to handle everything while in split-screen, so return a WCT even if it's empty. - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split" - + "Transition to handle request. triggerTask=%d type=%s mainChildren=%d" - + " sideChildren=%d", triggerTask.taskId, transitTypeToString(type), - mMainStage.getChildCount(), mSideStage.getChildCount()); - out = new WindowContainerTransaction(); - final StageTaskListener stage = getStageOfTask(triggerTask); - if (stage != null) { - // dismiss split if the last task in one of the stages is going away - if (isClosingType(type) && stage.getChildCount() == 1) { - // The top should be the opposite side that is closing: - mDismissTop = getStageType(stage) == STAGE_TYPE_MAIN - ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN; - } - } else { - if (triggerTask.getActivityType() == ACTIVITY_TYPE_HOME && isOpeningType(type)) { - // Going home so dismiss both. - mDismissTop = STAGE_TYPE_UNDEFINED; - } - } - if (mDismissTop != NO_DISMISS) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition " - + " deduced Dismiss from request. toTop=%s", - stageTypeToString(mDismissTop)); - prepareExitSplitScreen(mDismissTop, out); - mSplitTransitions.mPendingDismiss = transition; - } - } else { - // Not in split mode, so look for an open into a split stage just so we can whine and - // complain about how this isn't a supported operation. - if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) { - if (getStageOfTask(triggerTask) != null) { - throw new IllegalStateException("Entering split implicitly with only one task" - + " isn't supported."); - } - } - } - return out; - } - - @Override - public boolean startAnimation(@NonNull IBinder transition, - @NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction startTransaction, - @NonNull SurfaceControl.Transaction finishTransaction, - @NonNull Transitions.TransitionFinishCallback finishCallback) { - if (transition != mSplitTransitions.mPendingDismiss - && transition != mSplitTransitions.mPendingEnter) { - // Not entering or exiting, so just do some house-keeping and validation. - - // If we're not in split-mode, just abort so something else can handle it. - if (!isSplitScreenVisible()) return false; - - for (int iC = 0; iC < info.getChanges().size(); ++iC) { - final TransitionInfo.Change change = info.getChanges().get(iC); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo == null || !taskInfo.hasParentTask()) continue; - final StageTaskListener stage = getStageOfTask(taskInfo); - if (stage == null) continue; - if (isOpeningType(change.getMode())) { - if (!stage.containsTask(taskInfo.taskId)) { - Log.w(TAG, "Expected onTaskAppeared on " + stage + " to have been called" - + " with " + taskInfo.taskId + " before startAnimation()."); - } - } else if (isClosingType(change.getMode())) { - if (stage.containsTask(taskInfo.taskId)) { - Log.w(TAG, "Expected onTaskVanished on " + stage + " to have been called" - + " with " + taskInfo.taskId + " before startAnimation()."); - } - } - } - if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) { - // TODO(shell-transitions): Implement a fallback behavior for now. - throw new IllegalStateException("Somehow removed the last task in a stage" - + " outside of a proper transition"); - // This can happen in some pathological cases. For example: - // 1. main has 2 tasks [Task A (Single-task), Task B], side has one task [Task C] - // 2. Task B closes itself and starts Task A in LAUNCH_ADJACENT at the same time - // In this case, the result *should* be that we leave split. - // TODO(b/184679596): Find a way to either include task-org information in - // the transition, or synchronize task-org callbacks. - } - - // Use normal animations. - return false; - } - - boolean shouldAnimate = true; - if (mSplitTransitions.mPendingEnter == transition) { - shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction); - } else if (mSplitTransitions.mPendingDismiss == transition) { - shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction); - } - if (!shouldAnimate) return false; - - mSplitTransitions.playAnimation(transition, info, startTransaction, finishTransaction, - finishCallback, mMainStage.mRootTaskInfo.token, mSideStage.mRootTaskInfo.token); - return true; - } - - private boolean startPendingEnterAnimation(@NonNull IBinder transition, - @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { - if (info.getType() == TRANSIT_SPLIT_SCREEN_PAIR_OPEN) { - // First, verify that we actually have opened 2 apps in split. - TransitionInfo.Change mainChild = null; - TransitionInfo.Change sideChild = null; - for (int iC = 0; iC < info.getChanges().size(); ++iC) { - final TransitionInfo.Change change = info.getChanges().get(iC); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo == null || !taskInfo.hasParentTask()) continue; - final @SplitScreen.StageType int stageType = getStageType(getStageOfTask(taskInfo)); - if (stageType == STAGE_TYPE_MAIN) { - mainChild = change; - } else if (stageType == STAGE_TYPE_SIDE) { - sideChild = change; - } - } - if (mainChild == null || sideChild == null) { - throw new IllegalStateException("Launched 2 tasks in split, but didn't receive" - + " 2 tasks in transition. Possibly one of them failed to launch"); - // TODO: fallback logic. Probably start a new transition to exit split before - // applying anything here. Ideally consolidate with transition-merging. - } - - // Update local states (before animating). - setDividerVisibility(true); - setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */, - null /* wct */); - setSplitsVisible(true); - - addDividerBarToTransition(info, t, true /* show */); - - // Make some noise if things aren't totally expected. These states shouldn't effect - // transitions locally, but remotes (like Launcher) may get confused if they were - // depending on listener callbacks. This can happen because task-organizer callbacks - // aren't serialized with transition callbacks. - // TODO(b/184679596): Find a way to either include task-org information in - // the transition, or synchronize task-org callbacks. - if (!mMainStage.containsTask(mainChild.getTaskInfo().taskId)) { - Log.w(TAG, "Expected onTaskAppeared on " + mMainStage - + " to have been called with " + mainChild.getTaskInfo().taskId - + " before startAnimation()."); - } - if (!mSideStage.containsTask(sideChild.getTaskInfo().taskId)) { - Log.w(TAG, "Expected onTaskAppeared on " + mSideStage - + " to have been called with " + sideChild.getTaskInfo().taskId - + " before startAnimation()."); - } - return true; - } else { - // TODO: other entry method animations - throw new RuntimeException("Unsupported split-entry"); - } - } - - private boolean startPendingDismissAnimation(@NonNull IBinder transition, - @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) { - // Make some noise if things aren't totally expected. These states shouldn't effect - // transitions locally, but remotes (like Launcher) may get confused if they were - // depending on listener callbacks. This can happen because task-organizer callbacks - // aren't serialized with transition callbacks. - // TODO(b/184679596): Find a way to either include task-org information in - // the transition, or synchronize task-org callbacks. - if (mMainStage.getChildCount() != 0) { - final StringBuilder tasksLeft = new StringBuilder(); - for (int i = 0; i < mMainStage.getChildCount(); ++i) { - tasksLeft.append(i != 0 ? ", " : ""); - tasksLeft.append(mMainStage.mChildrenTaskInfo.keyAt(i)); - } - Log.w(TAG, "Expected onTaskVanished on " + mMainStage - + " to have been called with [" + tasksLeft.toString() - + "] before startAnimation()."); - } - if (mSideStage.getChildCount() != 0) { - final StringBuilder tasksLeft = new StringBuilder(); - for (int i = 0; i < mSideStage.getChildCount(); ++i) { - tasksLeft.append(i != 0 ? ", " : ""); - tasksLeft.append(mSideStage.mChildrenTaskInfo.keyAt(i)); - } - Log.w(TAG, "Expected onTaskVanished on " + mSideStage - + " to have been called with [" + tasksLeft.toString() - + "] before startAnimation()."); - } - - // Update local states. - setSplitsVisible(false); - // Wait until after animation to update divider - - if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) { - // Reset crops so they don't interfere with subsequent launches - t.setWindowCrop(mMainStage.mRootLeash, null); - t.setWindowCrop(mSideStage.mRootLeash, null); - } - - if (mDismissTop == STAGE_TYPE_UNDEFINED) { - // Going home (dismissing both splits) - - // TODO: Have a proper remote for this. Until then, though, reset state and use the - // normal animation stuff (which falls back to the normal launcher remote). - t.hide(mSplitLayout.getDividerLeash()); - setDividerVisibility(false); - mSplitTransitions.mPendingDismiss = null; - return false; - } - - addDividerBarToTransition(info, t, false /* show */); - // We're dismissing split by moving the other one to fullscreen. - // Since we don't have any animations for this yet, just use the internal example - // animations. - return true; - } - - private void addDividerBarToTransition(@NonNull TransitionInfo info, - @NonNull SurfaceControl.Transaction t, boolean show) { - final SurfaceControl leash = mSplitLayout.getDividerLeash(); - final TransitionInfo.Change barChange = new TransitionInfo.Change(null /* token */, leash); - final Rect bounds = mSplitLayout.getDividerBounds(); - barChange.setStartAbsBounds(bounds); - barChange.setEndAbsBounds(bounds); - barChange.setMode(show ? TRANSIT_TO_FRONT : TRANSIT_TO_BACK); - barChange.setFlags(FLAG_IS_DIVIDER_BAR); - // Technically this should be order-0, but this is running after layer assignment - // and it's a special case, so just add to end. - info.addChange(barChange); - // Be default, make it visible. The remote animator can adjust alpha if it plans to animate. - if (show) { - t.setAlpha(leash, 1.f); - t.setLayer(leash, Integer.MAX_VALUE); - t.setPosition(leash, bounds.left, bounds.top); - t.show(leash); - } - } - - RemoteAnimationTarget getDividerBarLegacyTarget() { - final Rect bounds = mSplitLayout.getDividerBounds(); - return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */, - mSplitLayout.getDividerLeash(), false /* isTranslucent */, null /* clipRect */, - null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */, - new android.graphics.Point(0, 0) /* position */, bounds, bounds, - new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */, - null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER); - } - - RemoteAnimationTarget getOutlineLegacyTarget() { - final Rect bounds = mSideStage.mRootTaskInfo.configuration.windowConfiguration.getBounds(); - // Leverage TYPE_DOCK_DIVIDER type when wrapping outline remote animation target in order to - // distinguish as a split auxiliary target in Launcher. - return new RemoteAnimationTarget(-1 /* taskId */, -1 /* mode */, - mSideStage.getOutlineLeash(), false /* isTranslucent */, null /* clipRect */, - null /* contentInsets */, Integer.MAX_VALUE /* prefixOrderIndex */, - new android.graphics.Point(0, 0) /* position */, bounds, bounds, - new WindowConfiguration(), true, null /* startLeash */, null /* startBounds */, - null /* taskInfo */, false /* allowEnterPip */, TYPE_DOCK_DIVIDER); - } - - @Override - public void dump(@NonNull PrintWriter pw, String prefix) { - final String innerPrefix = prefix + " "; - final String childPrefix = innerPrefix + " "; - pw.println(prefix + TAG + " mDisplayId=" + mDisplayId); - pw.println(innerPrefix + "mDividerVisible=" + mDividerVisible); - pw.println(innerPrefix + "MainStage"); - pw.println(childPrefix + "isActive=" + mMainStage.isActive()); - mMainStageListener.dump(pw, childPrefix); - pw.println(innerPrefix + "SideStage"); - mSideStageListener.dump(pw, childPrefix); - pw.println(innerPrefix + "mSplitLayout=" + mSplitLayout); - } - - /** - * Directly set the visibility of both splits. This assumes hasChildren matches visibility. - * This is intended for batch use, so it assumes other state management logic is already - * handled. - */ - private void setSplitsVisible(boolean visible) { - mMainStageListener.mVisible = mSideStageListener.mVisible = visible; - mMainStageListener.mHasChildren = mSideStageListener.mHasChildren = visible; - } - - /** - * Sets drag info to be logged when splitscreen is next entered. - */ - public void logOnDroppedToSplit(@SplitPosition int position, InstanceId dragSessionId) { - mLogger.enterRequestedByDrag(position, dragSessionId); - } - - /** - * Logs the exit of splitscreen. - */ - private void logExit(int exitReason) { - mLogger.logExit(exitReason, - SPLIT_POSITION_UNDEFINED, 0 /* mainStageUid */, - SPLIT_POSITION_UNDEFINED, 0 /* sideStageUid */, - mSplitLayout.isLandscape()); - } - - /** - * Logs the exit of splitscreen to a specific stage. This must be called before the exit is - * executed. - */ - private void logExitToStage(int exitReason, boolean toMainStage) { - mLogger.logExit(exitReason, - toMainStage ? getMainStagePosition() : SPLIT_POSITION_UNDEFINED, - toMainStage ? mMainStage.getTopChildTaskUid() : 0 /* mainStageUid */, - !toMainStage ? getSideStagePosition() : SPLIT_POSITION_UNDEFINED, - !toMainStage ? mSideStage.getTopChildTaskUid() : 0 /* sideStageUid */, - mSplitLayout.isLandscape()); - } - - class StageListenerImpl implements StageTaskListener.StageListenerCallbacks { - boolean mHasRootTask = false; - boolean mVisible = false; - boolean mHasChildren = false; - - @Override - public void onRootTaskAppeared() { - mHasRootTask = true; - StageCoordinator.this.onStageRootTaskAppeared(this); - } - - @Override - public void onStatusChanged(boolean visible, boolean hasChildren) { - if (!mHasRootTask) return; - - if (mHasChildren != hasChildren) { - mHasChildren = hasChildren; - StageCoordinator.this.onStageHasChildrenChanged(this); - } - if (mVisible != visible) { - mVisible = visible; - StageCoordinator.this.onStageVisibilityChanged(this); - } - } - - @Override - public void onChildTaskStatusChanged(int taskId, boolean present, boolean visible) { - StageCoordinator.this.onStageChildTaskStatusChanged(this, taskId, present, visible); - } - - @Override - public void onRootTaskVanished() { - reset(); - StageCoordinator.this.onStageRootTaskVanished(this); - } - - @Override - public void onNoLongerSupportMultiWindow() { - if (mMainStage.isActive()) { - StageCoordinator.this.exitSplitScreen(null /* childrenToTop */, - SPLITSCREEN_UICHANGED__EXIT_REASON__APP_DOES_NOT_SUPPORT_MULTIWINDOW); - } - } - - private void reset() { - mHasRootTask = false; - mVisible = false; - mHasChildren = false; - } - - public void dump(@NonNull PrintWriter pw, String prefix) { - pw.println(prefix + "mHasRootTask=" + mHasRootTask); - pw.println(prefix + "mVisible=" + mVisible); - pw.println(prefix + "mHasChildren=" + mHasChildren); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java deleted file mode 100644 index 7b679580fa87..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskListener.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.stagesplit; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; - -import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; - -import android.annotation.CallSuper; -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.graphics.Point; -import android.graphics.Rect; -import android.util.SparseArray; -import android.view.SurfaceControl; -import android.view.SurfaceSession; -import android.window.WindowContainerTransaction; - -import androidx.annotation.NonNull; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.SurfaceUtils; -import com.android.wm.shell.common.SyncTransactionQueue; - -import java.io.PrintWriter; - -/** - * Base class that handle common task org. related for split-screen stages. - * Note that this class and its sub-class do not directly perform hierarchy operations. - * They only serve to hold a collection of tasks and provide APIs like - * {@link #setBounds(Rect, WindowContainerTransaction)} for the centralized {@link StageCoordinator} - * to perform operations in-sync with other containers. - * - * @see StageCoordinator - */ -class StageTaskListener implements ShellTaskOrganizer.TaskListener { - private static final String TAG = StageTaskListener.class.getSimpleName(); - - protected static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD}; - protected static final int[] CONTROLLED_WINDOWING_MODES = - {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED}; - protected static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE = - {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW}; - - /** Callback interface for listening to changes in a split-screen stage. */ - public interface StageListenerCallbacks { - void onRootTaskAppeared(); - - void onStatusChanged(boolean visible, boolean hasChildren); - - void onChildTaskStatusChanged(int taskId, boolean present, boolean visible); - - void onRootTaskVanished(); - void onNoLongerSupportMultiWindow(); - } - - private final StageListenerCallbacks mCallbacks; - private final SurfaceSession mSurfaceSession; - protected final SyncTransactionQueue mSyncQueue; - - protected ActivityManager.RunningTaskInfo mRootTaskInfo; - protected SurfaceControl mRootLeash; - protected SurfaceControl mDimLayer; - protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>(); - private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>(); - - private final StageTaskUnfoldController mStageTaskUnfoldController; - - StageTaskListener(ShellTaskOrganizer taskOrganizer, int displayId, - StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue, - SurfaceSession surfaceSession, - @Nullable StageTaskUnfoldController stageTaskUnfoldController) { - mCallbacks = callbacks; - mSyncQueue = syncQueue; - mSurfaceSession = surfaceSession; - mStageTaskUnfoldController = stageTaskUnfoldController; - taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); - } - - int getChildCount() { - return mChildrenTaskInfo.size(); - } - - boolean containsTask(int taskId) { - return mChildrenTaskInfo.contains(taskId); - } - - /** - * Returns the top activity uid for the top child task. - */ - int getTopChildTaskUid() { - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - final ActivityManager.RunningTaskInfo info = mChildrenTaskInfo.valueAt(i); - if (info.topActivityInfo == null) { - continue; - } - return info.topActivityInfo.applicationInfo.uid; - } - return 0; - } - - /** @return {@code true} if this listener contains the currently focused task. */ - boolean isFocused() { - if (mRootTaskInfo == null) { - return false; - } - - if (mRootTaskInfo.isFocused) { - return true; - } - - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - if (mChildrenTaskInfo.valueAt(i).isFocused) { - return true; - } - } - - return false; - } - - @Override - @CallSuper - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - if (mRootTaskInfo == null && !taskInfo.hasParentTask()) { - mRootLeash = leash; - mRootTaskInfo = taskInfo; - mCallbacks.onRootTaskAppeared(); - sendStatusChanged(); - mSyncQueue.runInSync(t -> { - t.hide(mRootLeash); - mDimLayer = - SurfaceUtils.makeDimLayer(t, mRootLeash, "Dim layer", mSurfaceSession); - }); - } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { - final int taskId = taskInfo.taskId; - mChildrenLeashes.put(taskId, leash); - mChildrenTaskInfo.put(taskId, taskInfo); - updateChildTaskSurface(taskInfo, leash, true /* firstAppeared */); - mCallbacks.onChildTaskStatusChanged(taskId, true /* present */, taskInfo.isVisible); - if (ENABLE_SHELL_TRANSITIONS) { - // Status is managed/synchronized by the transition lifecycle. - return; - } - sendStatusChanged(); - } else { - throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo - + "\n mRootTaskInfo: " + mRootTaskInfo); - } - - if (mStageTaskUnfoldController != null) { - mStageTaskUnfoldController.onTaskAppeared(taskInfo, leash); - } - } - - @Override - @CallSuper - public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - if (!taskInfo.supportsMultiWindow) { - // Leave split screen if the task no longer supports multi window. - mCallbacks.onNoLongerSupportMultiWindow(); - return; - } - if (mRootTaskInfo.taskId == taskInfo.taskId) { - mRootTaskInfo = taskInfo; - } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { - mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); - mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */, - taskInfo.isVisible); - if (!ENABLE_SHELL_TRANSITIONS) { - updateChildTaskSurface( - taskInfo, mChildrenLeashes.get(taskInfo.taskId), false /* firstAppeared */); - } - } else { - throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo - + "\n mRootTaskInfo: " + mRootTaskInfo); - } - if (ENABLE_SHELL_TRANSITIONS) { - // Status is managed/synchronized by the transition lifecycle. - return; - } - sendStatusChanged(); - } - - @Override - @CallSuper - public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - final int taskId = taskInfo.taskId; - if (mRootTaskInfo.taskId == taskId) { - mCallbacks.onRootTaskVanished(); - mSyncQueue.runInSync(t -> t.remove(mDimLayer)); - mRootTaskInfo = null; - } else if (mChildrenTaskInfo.contains(taskId)) { - mChildrenTaskInfo.remove(taskId); - mChildrenLeashes.remove(taskId); - mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible); - if (ENABLE_SHELL_TRANSITIONS) { - // Status is managed/synchronized by the transition lifecycle. - return; - } - sendStatusChanged(); - } else { - throw new IllegalArgumentException(this + "\n Unknown task: " + taskInfo - + "\n mRootTaskInfo: " + mRootTaskInfo); - } - - if (mStageTaskUnfoldController != null) { - mStageTaskUnfoldController.onTaskVanished(taskInfo); - } - } - - @Override - public void attachChildSurfaceToTask(int taskId, SurfaceControl.Builder b) { - b.setParent(findTaskSurface(taskId)); - } - - @Override - public void reparentChildSurfaceToTask(int taskId, SurfaceControl sc, - SurfaceControl.Transaction t) { - t.reparent(sc, findTaskSurface(taskId)); - } - - private SurfaceControl findTaskSurface(int taskId) { - if (mRootTaskInfo.taskId == taskId) { - return mRootLeash; - } else if (mChildrenLeashes.contains(taskId)) { - return mChildrenLeashes.get(taskId); - } else { - throw new IllegalArgumentException("There is no surface for taskId=" + taskId); - } - } - - void setBounds(Rect bounds, WindowContainerTransaction wct) { - wct.setBounds(mRootTaskInfo.token, bounds); - } - - void reorderChild(int taskId, boolean onTop, WindowContainerTransaction wct) { - if (!containsTask(taskId)) { - return; - } - wct.reorder(mChildrenTaskInfo.get(taskId).token, onTop /* onTop */); - } - - void setVisibility(boolean visible, WindowContainerTransaction wct) { - wct.reorder(mRootTaskInfo.token, visible /* onTop */); - } - - void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener, - @SplitScreen.StageType int stage) { - for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { - int taskId = mChildrenTaskInfo.keyAt(i); - listener.onTaskStageChanged(taskId, stage, - mChildrenTaskInfo.get(taskId).isVisible); - } - } - - private void updateChildTaskSurface(ActivityManager.RunningTaskInfo taskInfo, - SurfaceControl leash, boolean firstAppeared) { - final Point taskPositionInParent = taskInfo.positionInParent; - mSyncQueue.runInSync(t -> { - t.setWindowCrop(leash, null); - t.setPosition(leash, taskPositionInParent.x, taskPositionInParent.y); - if (firstAppeared && !ENABLE_SHELL_TRANSITIONS) { - t.setAlpha(leash, 1f); - t.setMatrix(leash, 1, 0, 0, 1); - t.show(leash); - } - }); - } - - private void sendStatusChanged() { - mCallbacks.onStatusChanged(mRootTaskInfo.isVisible, mChildrenTaskInfo.size() > 0); - } - - @Override - @CallSuper - public void dump(@NonNull PrintWriter pw, String prefix) { - final String innerPrefix = prefix + " "; - final String childPrefix = innerPrefix + " "; - pw.println(prefix + this); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskUnfoldController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskUnfoldController.java deleted file mode 100644 index 62b9da6d4715..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/StageTaskUnfoldController.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.stagesplit; - -import static android.view.Display.DEFAULT_DISPLAY; - -import android.animation.RectEvaluator; -import android.animation.TypeEvaluator; -import android.annotation.NonNull; -import android.app.ActivityManager; -import android.content.Context; -import android.graphics.Rect; -import android.util.SparseArray; -import android.view.InsetsSource; -import android.view.InsetsState; -import android.view.SurfaceControl; - -import com.android.internal.policy.ScreenDecorationsUtils; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListener; -import com.android.wm.shell.common.TransactionPool; -import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; -import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener; -import com.android.wm.shell.unfold.UnfoldBackgroundController; - -import java.util.concurrent.Executor; - -/** - * Controls transformations of the split screen task surfaces in response - * to the unfolding/folding action on foldable devices - */ -public class StageTaskUnfoldController implements UnfoldListener, OnInsetsChangedListener { - - private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect()); - private static final float CROPPING_START_MARGIN_FRACTION = 0.05f; - - private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>(); - private final ShellUnfoldProgressProvider mUnfoldProgressProvider; - private final DisplayInsetsController mDisplayInsetsController; - private final UnfoldBackgroundController mBackgroundController; - private final Executor mExecutor; - private final int mExpandedTaskBarHeight; - private final float mWindowCornerRadiusPx; - private final Rect mStageBounds = new Rect(); - private final TransactionPool mTransactionPool; - - private InsetsSource mTaskbarInsetsSource; - private boolean mBothStagesVisible; - - public StageTaskUnfoldController(@NonNull Context context, - @NonNull TransactionPool transactionPool, - @NonNull ShellUnfoldProgressProvider unfoldProgressProvider, - @NonNull DisplayInsetsController displayInsetsController, - @NonNull UnfoldBackgroundController backgroundController, - @NonNull Executor executor) { - mUnfoldProgressProvider = unfoldProgressProvider; - mTransactionPool = transactionPool; - mExecutor = executor; - mBackgroundController = backgroundController; - mDisplayInsetsController = displayInsetsController; - mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context); - mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.taskbar_frame_height); - } - - /** - * Initializes the controller, starts listening for the external events - */ - public void init() { - mUnfoldProgressProvider.addListener(mExecutor, this); - mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this); - } - - @Override - public void insetsChanged(InsetsState insetsState) { - mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); - for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { - AnimationContext context = mAnimationContextByTaskId.valueAt(i); - context.update(); - } - } - - /** - * Called when split screen task appeared - * @param taskInfo info for the appeared task - * @param leash surface leash for the appeared task - */ - public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - AnimationContext context = new AnimationContext(leash); - mAnimationContextByTaskId.put(taskInfo.taskId, context); - } - - /** - * Called when a split screen task vanished - * @param taskInfo info for the vanished task - */ - public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId); - if (context != null) { - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - resetSurface(transaction, context); - transaction.apply(); - mTransactionPool.release(transaction); - } - mAnimationContextByTaskId.remove(taskInfo.taskId); - } - - @Override - public void onStateChangeProgress(float progress) { - if (mAnimationContextByTaskId.size() == 0 || !mBothStagesVisible) return; - - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - mBackgroundController.ensureBackground(transaction); - - for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { - AnimationContext context = mAnimationContextByTaskId.valueAt(i); - - context.mCurrentCropRect.set(RECT_EVALUATOR - .evaluate(progress, context.mStartCropRect, context.mEndCropRect)); - - transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect) - .setCornerRadius(context.mLeash, mWindowCornerRadiusPx); - } - - transaction.apply(); - - mTransactionPool.release(transaction); - } - - @Override - public void onStateChangeFinished() { - resetTransformations(); - } - - /** - * Called when split screen visibility changes - * @param bothStagesVisible true if both stages of the split screen are visible - */ - public void onSplitVisibilityChanged(boolean bothStagesVisible) { - mBothStagesVisible = bothStagesVisible; - if (!bothStagesVisible) { - resetTransformations(); - } - } - - /** - * Called when split screen stage bounds changed - * @param bounds new bounds for this stage - */ - public void onLayoutChanged(Rect bounds) { - mStageBounds.set(bounds); - - for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { - final AnimationContext context = mAnimationContextByTaskId.valueAt(i); - context.update(); - } - } - - private void resetTransformations() { - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - - for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { - final AnimationContext context = mAnimationContextByTaskId.valueAt(i); - resetSurface(transaction, context); - } - mBackgroundController.removeBackground(transaction); - transaction.apply(); - - mTransactionPool.release(transaction); - } - - private void resetSurface(SurfaceControl.Transaction transaction, AnimationContext context) { - transaction - .setWindowCrop(context.mLeash, null) - .setCornerRadius(context.mLeash, 0.0F); - } - - private class AnimationContext { - final SurfaceControl mLeash; - final Rect mStartCropRect = new Rect(); - final Rect mEndCropRect = new Rect(); - final Rect mCurrentCropRect = new Rect(); - - private AnimationContext(SurfaceControl leash) { - this.mLeash = leash; - update(); - } - - private void update() { - mStartCropRect.set(mStageBounds); - - if (mTaskbarInsetsSource != null) { - // Only insets the cropping window with taskbar when taskbar is expanded - if (mTaskbarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { - mStartCropRect.inset(mTaskbarInsetsSource - .calculateVisibleInsets(mStartCropRect)); - } - } - - // Offset to surface coordinates as layout bounds are in screen coordinates - mStartCropRect.offsetTo(0, 0); - - mEndCropRect.set(mStartCropRect); - - int maxSize = Math.max(mEndCropRect.width(), mEndCropRect.height()); - int margin = (int) (maxSize * CROPPING_START_MARGIN_FRACTION); - mStartCropRect.inset(margin, margin, margin, margin); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index 54d62edf2570..a0e176c7ea68 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -261,7 +261,8 @@ public class StartingSurfaceDrawer { WindowManager.LayoutParams.TYPE_APPLICATION_STARTING); params.setFitInsetsSides(0); params.setFitInsetsTypes(0); - params.format = PixelFormat.TRANSLUCENT; + params.format = suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN + ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT; int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java new file mode 100644 index 000000000000..1ffe26df729f --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2022 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.wm.shell.transition; + +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.view.WindowManager.TRANSIT_TO_BACK; +import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; + +import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; +import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP; +import static com.android.wm.shell.splitscreen.StageCoordinator.FLAG_IS_DIVIDER_BAR; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.view.WindowManager; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; +import android.window.WindowContainerTransaction; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.pip.PipTransitionController; +import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.splitscreen.StageCoordinator; + +import java.util.ArrayList; + +/** + * A handler for dealing with transitions involving multiple other handlers. For example: an + * activity in split-screen going into PiP. + */ +public class DefaultMixedHandler implements Transitions.TransitionHandler { + + private final Transitions mPlayer; + private final PipTransitionController mPipHandler; + private final StageCoordinator mSplitHandler; + + private static class MixedTransition { + static final int TYPE_ENTER_PIP_FROM_SPLIT = 1; + + final int mType; + final IBinder mTransition; + + Transitions.TransitionFinishCallback mFinishCallback = null; + + /** + * Mixed transitions are made up of multiple "parts". This keeps track of how many + * parts are currently animating. + */ + int mInFlightSubAnimations = 0; + + MixedTransition(int type, IBinder transition) { + mType = type; + mTransition = transition; + } + } + private final ArrayList<MixedTransition> mActiveTransitions = new ArrayList<>(); + + public DefaultMixedHandler(@NonNull Transitions player, + @NonNull PipTransitionController pipHandler, @NonNull StageCoordinator splitHandler) { + mPlayer = player; + mPipHandler = pipHandler; + mSplitHandler = splitHandler; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + if (mPipHandler.requestHasPipEnter(request) && mSplitHandler.isSplitActive()) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Got a PiP-enter request while " + + "Split-Screen is active, so treat it as Mixed."); + if (request.getRemoteTransition() != null) { + throw new IllegalStateException("Unexpected remote transition in" + + "pip-enter-from-split request"); + } + mActiveTransitions.add(new MixedTransition(MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT, + transition)); + + WindowContainerTransaction out = new WindowContainerTransaction(); + mPipHandler.augmentRequest(transition, request, out); + mSplitHandler.addEnterOrExitIfNeeded(request, out); + return out; + } + return null; + } + + private TransitionInfo subCopy(@NonNull TransitionInfo info, + @WindowManager.TransitionType int newType) { + final TransitionInfo out = new TransitionInfo(newType, info.getFlags()); + for (int i = 0; i < info.getChanges().size(); ++i) { + out.getChanges().add(info.getChanges().get(i)); + } + out.setRootLeash(info.getRootLeash(), info.getRootOffset().x, info.getRootOffset().y); + out.setAnimationOptions(info.getAnimationOptions()); + return out; + } + + private boolean isHomeOpening(@NonNull TransitionInfo.Change change) { + return change.getTaskInfo() != null + && change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME; + } + + private boolean isWallpaper(@NonNull TransitionInfo.Change change) { + return (change.getFlags() & FLAG_IS_WALLPAPER) != 0; + } + + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + MixedTransition mixed = null; + for (int i = mActiveTransitions.size() - 1; i >= 0; --i) { + if (mActiveTransitions.get(i).mTransition != transition) continue; + mixed = mActiveTransitions.remove(i); + break; + } + if (mixed == null) return false; + + if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { + return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction, + finishCallback); + } else { + throw new IllegalStateException("Starting mixed animation without a known mixed type? " + + mixed.mType); + } + } + + private boolean animateEnterPipFromSplit(@NonNull final MixedTransition mixed, + @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animating a mixed transition for " + + "entering PIP while Split-Screen is active."); + TransitionInfo.Change pipChange = null; + TransitionInfo.Change wallpaper = null; + final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK); + boolean homeIsOpening = false; + for (int i = info.getChanges().size() - 1; i >= 0; --i) { + TransitionInfo.Change change = info.getChanges().get(i); + if (mPipHandler.isEnteringPip(change, info.getType())) { + if (pipChange != null) { + throw new IllegalStateException("More than 1 pip-entering changes in one" + + " transition? " + info); + } + pipChange = change; + // going backwards, so remove-by-index is fine. + everythingElse.getChanges().remove(i); + } else if (isHomeOpening(change)) { + homeIsOpening = true; + } else if (isWallpaper(change)) { + wallpaper = change; + } + } + if (pipChange == null) { + // um, something probably went wrong. + return false; + } + final boolean isGoingHome = homeIsOpening; + mixed.mFinishCallback = finishCallback; + Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> { + --mixed.mInFlightSubAnimations; + if (mixed.mInFlightSubAnimations > 0) return; + if (isGoingHome) { + mSplitHandler.onTransitionAnimationComplete(); + } + mixed.mFinishCallback.onTransitionFinished(wct, wctCB); + }; + if (isGoingHome) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is actually mixed " + + "since entering-PiP caused us to leave split and return home."); + // We need to split the transition into 2 parts: the pip part (animated by pip) + // and the dismiss-part (animated by launcher). + mixed.mInFlightSubAnimations = 2; + // immediately make the wallpaper visible (so that we don't see it pop-in during + // the time it takes to start recents animation (which is remote). + if (wallpaper != null) { + startTransaction.show(wallpaper.getLeash()).setAlpha(wallpaper.getLeash(), 1.f); + } + // make a new startTransaction because pip's startEnterAnimation "consumes" it so + // we need a separate one to send over to launcher. + SurfaceControl.Transaction otherStartT = new SurfaceControl.Transaction(); + // Let split update internal state for dismiss. + mSplitHandler.prepareDismissAnimation(STAGE_TYPE_UNDEFINED, + EXIT_REASON_CHILD_TASK_ENTER_PIP, everythingElse, otherStartT, + finishTransaction); + + // We are trying to accommodate launcher's close animation which can't handle the + // divider-bar, so if split-handler is closing the divider-bar, just hide it and remove + // from transition info. + for (int i = everythingElse.getChanges().size() - 1; i >= 0; --i) { + if ((everythingElse.getChanges().get(i).getFlags() & FLAG_IS_DIVIDER_BAR) != 0) { + everythingElse.getChanges().remove(i); + break; + } + } + + mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction, + finishCB); + // Dispatch the rest of the transition normally. This will most-likely be taken by + // recents or default handler. + mPlayer.dispatchTransition(mixed.mTransition, everythingElse, otherStartT, + finishTransaction, finishCB, this); + } else { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Not leaving split, so just " + + "forward animation to Pip-Handler."); + // This happens if the pip-ing activity is in a multi-activity task (and thus a + // new pip task is spawned). In this case, we don't actually exit split so we can + // just let pip transition handle the animation verbatim. + mixed.mInFlightSubAnimations = 1; + mPipHandler.startAnimation(mixed.mTransition, info, startTransaction, finishTransaction, + finishCB); + } + return true; + } + + @Override + public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + } + + @Override + public void onTransitionMerged(@NonNull IBinder transition) { + MixedTransition mixed = null; + for (int i = mActiveTransitions.size() - 1; i >= 0; --i) { + if (mActiveTransitions.get(i).mTransition != transition) continue; + mixed = mActiveTransitions.remove(i); + break; + } + if (mixed == null) return; + if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { + mPipHandler.onTransitionMerged(transition); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 9154226b7b22..c3eaa8ee1da0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -24,6 +24,7 @@ import static android.app.ActivityOptions.ANIM_OPEN_CROSS_PROFILE_APPS; import static android.app.ActivityOptions.ANIM_SCALE_UP; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN; import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED; import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE; @@ -42,6 +43,7 @@ import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_RELAUNCH; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION; @@ -470,8 +472,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } final Rect clipRect = Transitions.isClosingType(change.getMode()) - ? mRotator.getEndBoundsInStartRotation(change) - : change.getEndAbsBounds(); + ? new Rect(mRotator.getEndBoundsInStartRotation(change)) + : new Rect(change.getEndAbsBounds()); + clipRect.offsetTo(0, 0); if (delayedEdgeExtension) { // If the edge extension needs to happen after the startTransition has been @@ -684,6 +687,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { final Rect endBounds = Transitions.isClosingType(changeMode) ? mRotator.getEndBoundsInStartRotation(change) : change.getEndAbsBounds(); + final boolean isDream = + isTask && change.getTaskInfo().topActivityType == ACTIVITY_TYPE_DREAM; if (info.isKeyguardGoingAway()) { a = mTransitionAnimation.loadKeyguardExitAnimation(flags, @@ -726,7 +731,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { } else { int animAttr = 0; boolean translucent = false; - if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) { + if (isDream) { + if (type == TRANSIT_OPEN) { + animAttr = enter + ? R.styleable.WindowAnimation_dreamActivityOpenEnterAnimation + : R.styleable.WindowAnimation_dreamActivityOpenExitAnimation; + } else if (type == TRANSIT_CLOSE) { + animAttr = enter + ? 0 + : R.styleable.WindowAnimation_dreamActivityCloseExitAnimation; + } + } else if (wallpaperTransit == WALLPAPER_TRANSITION_INTRA_OPEN) { animAttr = enter ? R.styleable.WindowAnimation_wallpaperIntraOpenEnterAnimation : R.styleable.WindowAnimation_wallpaperIntraOpenExitAnimation; @@ -790,6 +805,11 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { a = mTransitionAnimation.loadDefaultAnimationAttr(animAttr, translucent); } } + + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, + "loadAnimation: anim=%s animAttr=0x%x type=%s isEntrance=%b", a, animAttr, + transitTypeToString(type), + enter); } if (a != null) { @@ -954,7 +974,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private static void applyTransformation(long time, SurfaceControl.Transaction t, SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix, - Point position, float cornerRadius, @Nullable Rect clipRect) { + Point position, float cornerRadius, @Nullable Rect immutableClipRect) { anim.getTransformation(time, transformation); if (position != null) { transformation.getMatrix().postTranslate(position.x, position.y); @@ -962,6 +982,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { t.setMatrix(leash, transformation.getMatrix(), matrix); t.setAlpha(leash, transformation.getAlpha()); + final Rect clipRect = immutableClipRect == null ? null : new Rect(immutableClipRect); Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE); if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) { // Clip out any overflowing edge extension diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 435d67087f34..de0f47fa0a6b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; @@ -287,12 +288,14 @@ public class Transitions implements RemoteCallable<Transitions> { finishT.setAlpha(leash, 1.f); } } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { - // Wallpaper is a bit of an anomaly: it's visibility is tied to other WindowStates. - // As a result, we actually can't hide it's WindowToken because there may not be a - // transition associated with it becoming visible again. Fortunately, since it is - // always z-ordered to the back, we don't have to worry about it flickering to the - // front during reparenting, so the hide here isn't necessary for it. - if ((change.getFlags() & FLAG_IS_WALLPAPER) == 0) { + // Wallpaper/IME are anomalies: their visibility is tied to other WindowStates. + // As a result, we actually can't hide their WindowTokens because there may not be a + // transition associated with them becoming visible again. Fortunately, since + // wallpapers are always z-ordered to the back, we don't have to worry about it + // flickering to the front during reparenting. Similarly, the IME is reparented to + // the associated app, so its visibility is coupled. So, an explicit hide is not + // needed visually anyways. + if ((change.getFlags() & (FLAG_IS_WALLPAPER | FLAG_IS_INPUT_METHOD)) == 0) { finishT.hide(leash); } } @@ -309,13 +312,14 @@ public class Transitions implements RemoteCallable<Transitions> { if (info.getRootLeash().isValid()) { t.show(info.getRootLeash()); } + final int numChanges = info.getChanges().size(); // Put animating stuff above this line and put static stuff below it. - int zSplitLine = info.getChanges().size(); + final int zSplitLine = numChanges + 1; // changes should be ordered top-to-bottom in z - for (int i = info.getChanges().size() - 1; i >= 0; --i) { + for (int i = numChanges - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); final SurfaceControl leash = change.getLeash(); - final int mode = info.getChanges().get(i).getMode(); + final int mode = change.getMode(); // Don't reparent anything that isn't independent within its parents if (!TransitionInfo.isIndependent(change, info)) { @@ -329,26 +333,31 @@ public class Transitions implements RemoteCallable<Transitions> { t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x, change.getStartAbsBounds().top - info.getRootOffset().y); } + final int layer; // Put all the OPEN/SHOW on top - if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { + if ((change.getFlags() & FLAG_IS_WALLPAPER) != 0) { + // Wallpaper is always at the bottom. + layer = 0; + } else if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { if (isOpening) { // put on top - t.setLayer(leash, zSplitLine + info.getChanges().size() - i); + layer = zSplitLine + numChanges - i; } else { // put on bottom - t.setLayer(leash, zSplitLine - i); + layer = zSplitLine - i; } } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) { if (isOpening) { // put on bottom and leave visible - t.setLayer(leash, zSplitLine - i); + layer = zSplitLine - i; } else { // put on top - t.setLayer(leash, zSplitLine + info.getChanges().size() - i); + layer = zSplitLine + numChanges - i; } } else { // CHANGE or other - t.setLayer(leash, zSplitLine + info.getChanges().size() - i); + layer = zSplitLine + numChanges - i; } + t.setLayer(leash, layer); } } @@ -377,6 +386,7 @@ public class Transitions implements RemoteCallable<Transitions> { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s", transitionToken, info); t.apply(); + finishT.apply(); onAbort(transitionToken); return; } @@ -400,6 +410,7 @@ public class Transitions implements RemoteCallable<Transitions> { } if (nonTaskChange && transferStartingWindow) { t.apply(); + finishT.apply(); // Treat this as an abort since we are bypassing any merge logic and effectively // finishing immediately. onAbort(transitionToken); @@ -435,33 +446,42 @@ public class Transitions implements RemoteCallable<Transitions> { playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb)); } - boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) { - return handler.startAnimation(active.mToken, active.mInfo, active.mStartT, active.mFinishT, - (wct, cb) -> onFinish(active.mToken, wct, cb)); - } - - void playTransition(@NonNull ActiveTransition active) { + private void playTransition(@NonNull ActiveTransition active) { setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT); // If a handler already chose to run this animation, try delegating to it first. if (active.mHandler != null) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s", active.mHandler); - if (startAnimation(active, active.mHandler)) { + boolean consumed = active.mHandler.startAnimation(active.mToken, active.mInfo, + active.mStartT, active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb)); + if (consumed) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler"); return; } } - // Otherwise give every other handler a chance (in order) + // Otherwise give every other handler a chance + active.mHandler = dispatchTransition(active.mToken, active.mInfo, active.mStartT, + active.mFinishT, (wct, cb) -> onFinish(active.mToken, wct, cb), active.mHandler); + } + + /** + * Gives every handler (in order) a chance to animate until one consumes the transition. + * @return the handler which consumed the transition. + */ + TransitionHandler dispatchTransition(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, + @NonNull TransitionFinishCallback finishCB, @Nullable TransitionHandler skip) { for (int i = mHandlers.size() - 1; i >= 0; --i) { - if (mHandlers.get(i) == active.mHandler) continue; + if (mHandlers.get(i) == skip) continue; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s", mHandlers.get(i)); - if (startAnimation(active, mHandlers.get(i))) { + boolean consumed = mHandlers.get(i).startAnimation(transition, info, startT, finishT, + finishCB); + if (consumed) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s", mHandlers.get(i)); - active.mHandler = mHandlers.get(i); - return; + return mHandlers.get(i); } } throw new IllegalStateException( @@ -615,8 +635,9 @@ public class Transitions implements RemoteCallable<Transitions> { if (wct == null) { wct = new WindowContainerTransaction(); } - mDisplayController.getChangeController().dispatchOnRotateDisplay(wct, - change.getDisplayId(), change.getStartRotation(), change.getEndRotation()); + mDisplayController.getChangeController().dispatchOnDisplayChange(wct, + change.getDisplayId(), change.getStartRotation(), change.getEndRotation(), + null /* newDisplayAreaInfo */); } } active.mToken = mOrganizer.startTransition( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java index 9faf454261d3..86ca292399cb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldBackgroundController.java @@ -79,7 +79,7 @@ public class UnfoldBackgroundController { } private float[] getBackgroundColor(Context context) { - int colorInt = context.getResources().getColor(R.color.unfold_transition_background); + int colorInt = context.getResources().getColor(R.color.taskbar_background); return new float[]{ (float) red(colorInt) / 255.0F, (float) green(colorInt) / 255.0F, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java index 639603941c18..8e45e7d36b86 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java @@ -34,15 +34,20 @@ import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; import com.android.wm.shell.transition.Transitions.TransitionHandler; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider.UnfoldListener; +import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator; -import java.util.ArrayList; -import java.util.List; import java.util.concurrent.Executor; +/** + * Transition handler that is responsible for animating app surfaces when unfolding of foldable + * devices. It does not handle the folding animation, which is done in + * {@link com.android.wm.shell.fullscreen.FullscreenUnfoldController}. + */ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListener { private final ShellUnfoldProgressProvider mUnfoldProgressProvider; private final Transitions mTransitions; + private final UnfoldBackgroundController mUnfoldBackgroundController; private final Executor mExecutor; private final TransactionPool mTransactionPool; @@ -51,17 +56,22 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @Nullable private IBinder mTransition; - private final List<TransitionInfo.Change> mAnimatedFullscreenTasks = new ArrayList<>(); + private final FullscreenUnfoldTaskAnimator mFullscreenAnimator; public UnfoldTransitionHandler(ShellUnfoldProgressProvider unfoldProgressProvider, - TransactionPool transactionPool, Executor executor, Transitions transitions) { + FullscreenUnfoldTaskAnimator animator, TransactionPool transactionPool, + UnfoldBackgroundController unfoldBackgroundController, + Executor executor, Transitions transitions) { mUnfoldProgressProvider = unfoldProgressProvider; + mFullscreenAnimator = animator; mTransactionPool = transactionPool; + mUnfoldBackgroundController = unfoldBackgroundController; mExecutor = executor; mTransitions = transitions; } public void init() { + mFullscreenAnimator.init(); mTransitions.addHandler(this); mUnfoldProgressProvider.addListener(mExecutor, this); } @@ -71,40 +81,36 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback) { - if (transition != mTransition) return false; + mUnfoldBackgroundController.ensureBackground(startTransaction); startTransaction.apply(); - mAnimatedFullscreenTasks.clear(); + mFullscreenAnimator.clearTasks(); info.getChanges().forEach(change -> { final boolean allowedToAnimate = change.getTaskInfo() != null + && change.getTaskInfo().isVisible() && change.getTaskInfo().getWindowingMode() == WINDOWING_MODE_FULLSCREEN && change.getTaskInfo().getActivityType() != ACTIVITY_TYPE_HOME && change.getMode() == TRANSIT_CHANGE; if (allowedToAnimate) { - mAnimatedFullscreenTasks.add(change); + mFullscreenAnimator.addTask(change.getTaskInfo(), change.getLeash()); } }); + mFullscreenAnimator.resetAllSurfaces(finishTransaction); + mUnfoldBackgroundController.removeBackground(finishTransaction); mFinishCallback = finishCallback; - mTransition = null; return true; } @Override public void onStateChangeProgress(float progress) { - mAnimatedFullscreenTasks.forEach(change -> { - final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); - - // TODO: this is a placeholder animation, replace with a spec version in the next CLs - final float testScale = 0.8f + 0.2f * progress; - transaction.setScale(change.getLeash(), testScale, testScale); - - transaction.apply(); - mTransactionPool.release(transaction); - }); + final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); + mFullscreenAnimator.applyAnimationProgress(progress, transaction); + transaction.apply(); + mTransactionPool.release(transaction); } @Override @@ -112,7 +118,8 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene if (mFinishCallback != null) { mFinishCallback.onTransitionFinished(null, null); mFinishCallback = null; - mAnimatedFullscreenTasks.clear(); + mTransition = null; + mFullscreenAnimator.clearTasks(); } } @@ -127,4 +134,8 @@ public class UnfoldTransitionHandler implements TransitionHandler, UnfoldListene } return null; } + + public boolean willHandleTransition() { + return mTransition != null; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java new file mode 100644 index 000000000000..6ec5512c22ec --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/FullscreenUnfoldTaskAnimator.java @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2022 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.wm.shell.unfold.animation; + +import static android.util.MathUtils.lerp; +import static android.view.Display.DEFAULT_DISPLAY; + +import android.animation.RectEvaluator; +import android.animation.TypeEvaluator; +import android.app.TaskInfo; +import android.content.Context; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.util.SparseArray; +import android.view.InsetsSource; +import android.view.InsetsState; +import android.view.SurfaceControl; +import android.view.SurfaceControl.Transaction; + +import com.android.internal.policy.ScreenDecorationsUtils; +import com.android.wm.shell.common.DisplayInsetsController; + +/** + * This helper class contains logic that calculates scaling and cropping parameters + * for the folding/unfolding animation. As an input it receives TaskInfo objects and + * surfaces leashes and as an output it could fill surface transactions with required + * transformations. + * + * This class is used by + * {@link com.android.wm.shell.unfold.UnfoldTransitionHandler} and + * {@link com.android.wm.shell.fullscreen.FullscreenUnfoldController}. They use independent + * instances of FullscreenUnfoldTaskAnimator. + */ +public class FullscreenUnfoldTaskAnimator implements + DisplayInsetsController.OnInsetsChangedListener { + + private static final float[] FLOAT_9 = new float[9]; + private static final TypeEvaluator<Rect> RECT_EVALUATOR = new RectEvaluator(new Rect()); + + private static final float HORIZONTAL_START_MARGIN = 0.08f; + private static final float VERTICAL_START_MARGIN = 0.03f; + private static final float END_SCALE = 1f; + private static final float START_SCALE = END_SCALE - VERTICAL_START_MARGIN * 2; + + private final SparseArray<AnimationContext> mAnimationContextByTaskId = new SparseArray<>(); + private final int mExpandedTaskBarHeight; + private final float mWindowCornerRadiusPx; + private final DisplayInsetsController mDisplayInsetsController; + + private InsetsSource mTaskbarInsetsSource; + + public FullscreenUnfoldTaskAnimator(Context context, + DisplayInsetsController displayInsetsController) { + mDisplayInsetsController = displayInsetsController; + mExpandedTaskBarHeight = context.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.taskbar_frame_height); + mWindowCornerRadiusPx = ScreenDecorationsUtils.getWindowCornerRadius(context); + } + + public void init() { + mDisplayInsetsController.addInsetsChangedListener(DEFAULT_DISPLAY, this); + } + + @Override + public void insetsChanged(InsetsState insetsState) { + mTaskbarInsetsSource = insetsState.getSource(InsetsState.ITYPE_EXTRA_NAVIGATION_BAR); + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + AnimationContext context = mAnimationContextByTaskId.valueAt(i); + context.update(mTaskbarInsetsSource, context.mTaskInfo); + } + } + + public boolean hasActiveTasks() { + return mAnimationContextByTaskId.size() > 0; + } + + public void addTask(TaskInfo taskInfo, SurfaceControl leash) { + AnimationContext animationContext = new AnimationContext(leash, mTaskbarInsetsSource, + taskInfo); + mAnimationContextByTaskId.put(taskInfo.taskId, animationContext); + } + + public void onTaskInfoChanged(TaskInfo taskInfo) { + AnimationContext animationContext = mAnimationContextByTaskId.get(taskInfo.taskId); + if (animationContext != null) { + animationContext.update(mTaskbarInsetsSource, taskInfo); + } + } + + public void removeTask(TaskInfo taskInfo) { + mAnimationContextByTaskId.remove(taskInfo.taskId); + } + + public void clearTasks() { + mAnimationContextByTaskId.clear(); + } + + public void resetSurface(TaskInfo taskInfo, Transaction transaction) { + final AnimationContext context = mAnimationContextByTaskId.get(taskInfo.taskId); + if (context != null) { + resetSurface(context, transaction); + } + } + + public void applyAnimationProgress(float progress, Transaction transaction) { + if (mAnimationContextByTaskId.size() == 0) return; + + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + final AnimationContext context = mAnimationContextByTaskId.valueAt(i); + + context.mCurrentCropRect.set(RECT_EVALUATOR + .evaluate(progress, context.mStartCropRect, context.mEndCropRect)); + + float scale = lerp(START_SCALE, END_SCALE, progress); + context.mMatrix.setScale(scale, scale, context.mCurrentCropRect.exactCenterX(), + context.mCurrentCropRect.exactCenterY()); + + transaction.setWindowCrop(context.mLeash, context.mCurrentCropRect) + .setMatrix(context.mLeash, context.mMatrix, FLOAT_9) + .setCornerRadius(context.mLeash, mWindowCornerRadiusPx) + .show(context.mLeash) + ; + } + } + + public void resetAllSurfaces(Transaction transaction) { + for (int i = mAnimationContextByTaskId.size() - 1; i >= 0; i--) { + final AnimationContext context = mAnimationContextByTaskId.valueAt(i); + resetSurface(context, transaction); + } + } + + private void resetSurface(AnimationContext context, Transaction transaction) { + transaction + .setWindowCrop(context.mLeash, null) + .setCornerRadius(context.mLeash, 0.0F) + .setMatrix(context.mLeash, 1.0F, 0.0F, 0.0F, 1.0F) + .setPosition(context.mLeash, + (float) context.mTaskInfo.positionInParent.x, + (float) context.mTaskInfo.positionInParent.y); + } + + private class AnimationContext { + final SurfaceControl mLeash; + final Rect mStartCropRect = new Rect(); + final Rect mEndCropRect = new Rect(); + final Rect mCurrentCropRect = new Rect(); + final Matrix mMatrix = new Matrix(); + + TaskInfo mTaskInfo; + + private AnimationContext(SurfaceControl leash, InsetsSource taskBarInsetsSource, + TaskInfo taskInfo) { + mLeash = leash; + update(taskBarInsetsSource, taskInfo); + } + + private void update(InsetsSource taskBarInsetsSource, TaskInfo taskInfo) { + mTaskInfo = taskInfo; + mStartCropRect.set(mTaskInfo.getConfiguration().windowConfiguration.getBounds()); + + if (taskBarInsetsSource != null) { + // Only insets the cropping window with task bar when it's expanded + if (taskBarInsetsSource.getFrame().height() >= mExpandedTaskBarHeight) { + mStartCropRect.inset(taskBarInsetsSource + .calculateVisibleInsets(mStartCropRect)); + } + } + + mEndCropRect.set(mStartCropRect); + + int horizontalMargin = (int) (mEndCropRect.width() * HORIZONTAL_START_MARGIN); + mStartCropRect.left = mEndCropRect.left + horizontalMargin; + mStartCropRect.right = mEndCropRect.right - horizontalMargin; + int verticalMargin = (int) (mEndCropRect.height() * VERTICAL_START_MARGIN); + mStartCropRect.top = mEndCropRect.top + verticalMargin; + mStartCropRect.bottom = mEndCropRect.bottom - verticalMargin; + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index cb478c84c2b7..5dacdbf4b638 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -43,6 +43,80 @@ fun FlickerTestParameter.appPairsDividerBecomesVisible() { } } +fun FlickerTestParameter.splitScreenDividerBecomesVisible() { + layerBecomesVisible(SPLIT_SCREEN_DIVIDER_COMPONENT) +} + +fun FlickerTestParameter.layerBecomesVisible( + component: FlickerComponentName +) { + assertLayers { + this.isInvisible(component) + .then() + .isVisible(component) + } +} + +fun FlickerTestParameter.layerIsVisibleAtEnd( + component: FlickerComponentName +) { + assertLayersEnd { + this.isVisible(component) + } +} + +fun FlickerTestParameter.splitAppLayerBoundsBecomesVisible( + rotation: Int, + component: FlickerComponentName, + splitLeftTop: Boolean +) { + assertLayers { + this.isInvisible(component) + .then() + .invoke("splitAppLayerBoundsBecomesVisible") { + val dividerRegion = it.layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region + it.visibleRegion(component).overlaps(if (splitLeftTop) { + getSplitLeftTopRegion(dividerRegion, rotation) + } else { + getSplitRightBottomRegion(dividerRegion, rotation) + }) + } + } +} + +fun FlickerTestParameter.splitAppLayerBoundsIsVisibleAtEnd( + rotation: Int, + component: FlickerComponentName, + splitLeftTop: Boolean +) { + assertLayersEnd { + val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region + visibleRegion(component).overlaps(if (splitLeftTop) { + getSplitLeftTopRegion(dividerRegion, rotation) + } else { + getSplitRightBottomRegion(dividerRegion, rotation) + }) + } +} + +fun FlickerTestParameter.appWindowBecomesVisible( + component: FlickerComponentName +) { + assertWm { + this.isAppWindowInvisible(component) + .then() + .isAppWindowVisible(component) + } +} + +fun FlickerTestParameter.appWindowIsVisibleAtEnd( + component: FlickerComponentName +) { + assertWmEnd { + this.isAppWindowVisible(component) + } +} + fun FlickerTestParameter.dockedStackDividerIsVisibleAtEnd() { assertLayersEnd { this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) @@ -135,4 +209,24 @@ fun getSecondaryRegion(dividerRegion: Region, rotation: Int): Region { Region.from(dividerRegion.bounds.right - WindowUtils.dockedStackDividerInset, 0, displayBounds.bounds.right, displayBounds.bounds.bottom) } -}
\ No newline at end of file +} + +fun getSplitLeftTopRegion(dividerRegion: Region, rotation: Int): Region { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + return if (displayBounds.width > displayBounds.height) { + Region.from(0, 0, dividerRegion.bounds.left, displayBounds.bounds.bottom) + } else { + Region.from(0, 0, displayBounds.bounds.right, dividerRegion.bounds.top) + } +} + +fun getSplitRightBottomRegion(dividerRegion: Region, rotation: Int): Region { + val displayBounds = WindowUtils.getDisplayBounds(rotation) + return if (displayBounds.width > displayBounds.height) { + Region.from(dividerRegion.bounds.right, 0, displayBounds.bounds.right, + displayBounds.bounds.bottom) + } else { + Region.from(0, dividerRegion.bounds.bottom, displayBounds.bounds.right, + displayBounds.bounds.bottom) + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt index 40891f36a5da..f56eb6e783aa 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt @@ -21,4 +21,5 @@ import com.android.server.wm.traces.common.FlickerComponentName const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui" val APP_PAIR_SPLIT_DIVIDER_COMPONENT = FlickerComponentName("", "AppPairSplitDivider#") -val DOCKED_STACK_DIVIDER_COMPONENT = FlickerComponentName("", "DockedStackDivider#")
\ No newline at end of file +val DOCKED_STACK_DIVIDER_COMPONENT = FlickerComponentName("", "DockedStackDivider#") +val SPLIT_SCREEN_DIVIDER_COMPONENT = FlickerComponentName("", "StageCoordinatorSplitDivider#") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt deleted file mode 100644 index c9cab39b7d8b..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.apppairs - -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group1 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd -import com.android.wm.shell.flicker.helpers.AppPairsHelper -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow -import org.junit.After -import org.junit.Before -import org.junit.FixMethodOrder -import org.junit.Ignore -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test cold launch app from launcher. When the device doesn't support non-resizable in multi window - * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs should not pair - * non-resizable apps. - * - * To run this test: `atest WMShellFlickerTests:AppPairsTestCannotPairNonResizeableApps` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group1 -class AppPairsTestCannotPairNonResizeableApps( - testSpec: FlickerTestParameter -) : AppPairsTransition(testSpec) { - - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - nonResizeableApp?.launchViaIntent(wmHelper) - // TODO pair apps through normal UX flow - executeShellCommand( - composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true)) - nonResizeableApp?.run { wmHelper.waitForFullScreenApp(nonResizeableApp.component) } - } - } - - @Before - override fun setup() { - super.setup() - setSupportsNonResizableMultiWindow(instrumentation, -1) - } - - @After - override fun teardown() { - super.teardown() - resetMultiWindowConfig(instrumentation) - } - - @Ignore - @Test - override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() - - @Ignore - @Test - override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() - - @Ignore - @Test - override fun navBarLayerIsVisible() = super.navBarLayerIsVisible() - - @Ignore - @Test - fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd() - - @Ignore - @Test - fun onlyResizeableAppWindowVisible() { - val nonResizeableApp = nonResizeableApp - require(nonResizeableApp != null) { - "Non resizeable app not initialized" - } - testSpec.assertWmEnd { - isAppWindowVisible(nonResizeableApp.component) - isAppWindowInvisible(primaryApp.component) - } - } - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = AppPairsHelper.TEST_REPETITIONS) - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt deleted file mode 100644 index 60c32c99d1ff..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.apppairs - -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group1 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT -import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.AppPairsHelper -import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown -import org.junit.FixMethodOrder -import org.junit.Ignore -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test cold launch app from launcher. - * To run this test: `atest WMShellFlickerTests:AppPairsTestPairPrimaryAndSecondaryApps` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group1 -class AppPairsTestPairPrimaryAndSecondaryApps( - testSpec: FlickerTestParameter -) : AppPairsTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - // TODO pair apps through normal UX flow - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) - waitAppsShown(primaryApp, secondaryApp) - } - } - - @Ignore - @Test - override fun navBarLayerIsVisible() = super.navBarLayerIsVisible() - - @Ignore - @Test - override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() - - @Ignore - @Test - override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() - - @Ignore - @Test - fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd() - - @Ignore - @Test - fun bothAppWindowsVisible() { - testSpec.assertWmEnd { - isAppWindowVisible(primaryApp.component) - isAppWindowVisible(secondaryApp.component) - } - } - - @Ignore - @Test - fun appsEndingBounds() { - testSpec.assertLayersEnd { - val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region - visibleRegion(primaryApp.component) - .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion)) - visibleRegion(secondaryApp.component) - .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion)) - } - } - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = AppPairsHelper.TEST_REPETITIONS) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt deleted file mode 100644 index 24869a802167..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestSupportPairNonResizeableApps.kt +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.apppairs - -import android.view.Display -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group1 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.traces.common.WindowManagerConditionsFactory -import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.AppPairsHelper -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow -import org.junit.After -import org.junit.Before -import org.junit.FixMethodOrder -import org.junit.Ignore -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test cold launch app from launcher. When the device supports non-resizable in multi window - * {@link Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW}, app pairs can pair - * non-resizable apps. - * - * To run this test: `atest WMShellFlickerTests:AppPairsTestSupportPairNonResizeableApps` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group1 -class AppPairsTestSupportPairNonResizeableApps( - testSpec: FlickerTestParameter -) : AppPairsTransition(testSpec) { - - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - nonResizeableApp?.launchViaIntent(wmHelper) - // TODO pair apps through normal UX flow - executeShellCommand( - composePairsCommand(primaryTaskId, nonResizeableTaskId, pair = true)) - val waitConditions = mutableListOf( - WindowManagerConditionsFactory.isWindowVisible(primaryApp.component), - WindowManagerConditionsFactory.isLayerVisible(primaryApp.component), - WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY)) - - nonResizeableApp?.let { - waitConditions.add( - WindowManagerConditionsFactory.isWindowVisible(nonResizeableApp.component)) - waitConditions.add( - WindowManagerConditionsFactory.isLayerVisible(nonResizeableApp.component)) - } - wmHelper.waitFor(*waitConditions.toTypedArray()) - } - } - - @Before - override fun setup() { - super.setup() - setSupportsNonResizableMultiWindow(instrumentation, 1) - } - - @After - override fun teardown() { - super.teardown() - resetMultiWindowConfig(instrumentation) - } - - @Ignore - @Test - override fun navBarLayerIsVisible() = super.navBarLayerIsVisible() - - @Ignore - @Test - override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() - - @Ignore - @Test - override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() - - @Ignore - @Test - fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd() - - @Ignore - @Test - fun bothAppWindowVisible() { - val nonResizeableApp = nonResizeableApp - require(nonResizeableApp != null) { - "Non resizeable app not initialized" - } - testSpec.assertWmEnd { - isAppWindowVisible(nonResizeableApp.component) - isAppWindowVisible(primaryApp.component) - } - } - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = AppPairsHelper.TEST_REPETITIONS) - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt deleted file mode 100644 index 007415d19860..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.apppairs - -import android.os.SystemClock -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group1 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.wm.shell.flicker.APP_PAIR_SPLIT_DIVIDER_COMPONENT -import com.android.wm.shell.flicker.appPairsDividerIsInvisibleAtEnd -import com.android.wm.shell.flicker.helpers.AppPairsHelper -import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown -import org.junit.FixMethodOrder -import org.junit.Ignore -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test cold launch app from launcher. - * To run this test: `atest WMShellFlickerTests:AppPairsTestUnpairPrimaryAndSecondaryApps` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group1 -class AppPairsTestUnpairPrimaryAndSecondaryApps( - testSpec: FlickerTestParameter -) : AppPairsTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - setup { - eachRun { - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) - waitAppsShown(primaryApp, secondaryApp) - } - } - transitions { - // TODO pair apps through normal UX flow - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = false)) - SystemClock.sleep(AppPairsHelper.TIMEOUT_MS) - } - } - - @Ignore - @Test - override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() - - @Ignore - @Test - override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() - - @Ignore - @Test - fun appPairsDividerIsInvisibleAtEnd() = testSpec.appPairsDividerIsInvisibleAtEnd() - - @Ignore - @Test - fun bothAppWindowsInvisible() { - testSpec.assertWmEnd { - isAppWindowInvisible(primaryApp.component) - isAppWindowInvisible(secondaryApp.component) - } - } - - @Ignore - @Test - fun appsStartingBounds() { - testSpec.assertLayersStart { - val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region - visibleRegion(primaryApp.component) - .coversExactly(appPairsHelper.getPrimaryBounds(dividerRegion)) - visibleRegion(secondaryApp.component) - .coversExactly(appPairsHelper.getSecondaryBounds(dividerRegion)) - } - } - - @Ignore - @Test - fun appsEndingBounds() { - testSpec.assertLayersEnd { - notContains(primaryApp.component) - notContains(secondaryApp.component) - } - } - - @Ignore - @Test - override fun navBarLayerIsVisible() = super.navBarLayerIsVisible() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): List<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = AppPairsHelper.TEST_REPETITIONS) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt deleted file mode 100644 index 3e17948b4a84..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.apppairs - -import android.app.Instrumentation -import android.content.Context -import android.system.helpers.ActivityHelper -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerBuilderProvider -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.navBarLayerIsVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarLayerIsVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.wm.shell.flicker.helpers.AppPairsHelper -import com.android.wm.shell.flicker.helpers.BaseAppHelper -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import com.android.wm.shell.flicker.testapp.Components -import org.junit.After -import org.junit.Before -import org.junit.Ignore -import org.junit.Test - -abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) { - protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - protected val context: Context = instrumentation.context - protected val activityHelper = ActivityHelper.getInstance() - protected val appPairsHelper = AppPairsHelper(instrumentation, - Components.SplitScreenActivity.LABEL, - Components.SplitScreenActivity.COMPONENT.toFlickerComponent()) - - protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation) - protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) - protected open val nonResizeableApp: SplitScreenHelper? = - SplitScreenHelper.getNonResizeable(instrumentation) - protected var primaryTaskId = "" - protected var secondaryTaskId = "" - protected var nonResizeableTaskId = "" - private var prevDevEnableNonResizableMultiWindow = 0 - - @Before - open fun setup() { - prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context) - if (prevDevEnableNonResizableMultiWindow != 0) { - // Turn off the development option - setDevEnableNonResizableMultiWindow(context, 0) - } - } - - @After - open fun teardown() { - setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow) - } - - @FlickerBuilderProvider - fun buildFlicker(): FlickerBuilder { - return FlickerBuilder(instrumentation).apply { - transition(this) - } - } - - internal open val transition: FlickerBuilder.() -> Unit - get() = { - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - this.setRotation(testSpec.startRotation) - primaryApp.launchViaIntent(wmHelper) - secondaryApp.launchViaIntent(wmHelper) - nonResizeableApp?.launchViaIntent(wmHelper) - updateTasksId() - } - } - teardown { - eachRun { - executeShellCommand(composePairsCommand( - primaryTaskId, secondaryTaskId, pair = false)) - executeShellCommand(composePairsCommand( - primaryTaskId, nonResizeableTaskId, pair = false)) - primaryApp.exit(wmHelper) - secondaryApp.exit(wmHelper) - nonResizeableApp?.exit(wmHelper) - } - } - } - - protected fun updateTasksId() { - primaryTaskId = getTaskIdForActivity( - primaryApp.component.packageName, primaryApp.component.className).toString() - secondaryTaskId = getTaskIdForActivity( - secondaryApp.component.packageName, secondaryApp.component.className).toString() - val nonResizeableApp = nonResizeableApp - if (nonResizeableApp != null) { - nonResizeableTaskId = getTaskIdForActivity( - nonResizeableApp.component.packageName, - nonResizeableApp.component.className).toString() - } - } - - private fun getTaskIdForActivity(pkgName: String, activityName: String): Int { - return activityHelper.getTaskIdForActivity(pkgName, activityName) - } - - internal fun executeShellCommand(cmd: String) { - BaseAppHelper.executeShellCommand(instrumentation, cmd) - } - - internal fun composePairsCommand( - primaryApp: String, - secondaryApp: String, - pair: Boolean - ): String = buildString { - // dumpsys activity service SystemUIService WMShell {pair|unpair} ${TASK_ID_1} ${TASK_ID_2} - append("dumpsys activity service SystemUIService WMShell ") - if (pair) { - append("pair ") - } else { - append("unpair ") - } - append("$primaryApp $secondaryApp") - } - - @Ignore - @Test - open fun navBarLayerIsVisible() { - testSpec.navBarLayerIsVisible() - } - - @Ignore - @Test - open fun statusBarLayerIsVisible() { - testSpec.statusBarLayerIsVisible() - } - - @Ignore - @Test - open fun navBarWindowIsVisible() { - testSpec.navBarWindowIsVisible() - } - - @Ignore - @Test - open fun statusBarWindowIsVisible() { - testSpec.statusBarWindowIsVisible() - } - - @Ignore - @Test - open fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales() - - @Ignore - @Test - open fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales() -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS deleted file mode 100644 index 8446b37dbf06..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# window manager > wm shell > Split Screen -# Bug component: 928697 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt deleted file mode 100644 index b0c3ba20d948..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.apppairs - -import android.view.Surface -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group1 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.setRotation -import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Ignore -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open apps to app pairs and rotate. - * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsInAppPairsMode` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group1 -class RotateTwoLaunchedAppsInAppPairsMode( - testSpec: FlickerTestParameter -) : RotateTwoLaunchedAppsTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - executeShellCommand(composePairsCommand( - primaryTaskId, secondaryTaskId, true /* pair */)) - waitAppsShown(primaryApp, secondaryApp) - setRotation(testSpec.endRotation) - } - } - - @Ignore - @Test - override fun navBarLayerIsVisible() = super.navBarLayerIsVisible() - - @Ignore - @Test - override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible() - - @Ignore - @Test - fun bothAppWindowsVisible() { - testSpec.assertWmEnd { - isAppWindowVisible(primaryApp.component) - isAppWindowVisible(secondaryApp.component) - } - } - - @Ignore - @Test - fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd() - - @Ignore - @Test - fun appPairsPrimaryBoundsIsVisibleAtEnd() = - testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation, - primaryApp.component) - - @Ignore - @Test - fun appPairsSecondaryBoundsIsVisibleAtEnd() = - testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation, - secondaryApp.component) - - @Ignore - @Test - override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270) - ) - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt deleted file mode 100644 index ae56c7732a4d..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.apppairs - -import android.view.Surface -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group1 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.setRotation -import com.android.wm.shell.flicker.appPairsDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.AppPairsHelper.Companion.waitAppsShown -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Ignore -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open apps to app pairs and rotate. - * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppsRotateAndEnterAppPairsMode` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group1 -class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode( - testSpec: FlickerTestParameter -) : RotateTwoLaunchedAppsTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - this.setRotation(testSpec.endRotation) - executeShellCommand( - composePairsCommand(primaryTaskId, secondaryTaskId, pair = true)) - waitAppsShown(primaryApp, secondaryApp) - } - } - - @Ignore - @Test - fun appPairsDividerIsVisibleAtEnd() = testSpec.appPairsDividerIsVisibleAtEnd() - - @Ignore - @Test - override fun navBarWindowIsVisible() = super.navBarWindowIsVisible() - - @Ignore - @Test - override fun navBarLayerIsVisible() = super.navBarLayerIsVisible() - - @Ignore - @Test - override fun statusBarWindowIsVisible() = super.statusBarWindowIsVisible() - - @Ignore - @Test - override fun statusBarLayerIsVisible() = super.statusBarLayerIsVisible() - - @Ignore - @Test - override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() - - @Ignore - @Test - fun bothAppWindowsVisible() { - testSpec.assertWmEnd { - isAppWindowVisible(primaryApp.component) - isAppWindowVisible(secondaryApp.component) - } - } - - @Ignore - @Test - fun appPairsPrimaryBoundsIsVisibleAtEnd() = - testSpec.appPairsPrimaryBoundsIsVisibleAtEnd(testSpec.endRotation, - primaryApp.component) - - @Ignore - @Test - fun appPairsSecondaryBoundsIsVisibleAtEnd() = - testSpec.appPairsSecondaryBoundsIsVisibleAtEnd(testSpec.endRotation, - secondaryApp.component) - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_90, Surface.ROTATION_270) - ) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt deleted file mode 100644 index b1f1c9e539df..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsTransition.kt +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.apppairs - -import android.view.Surface -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.Assume.assumeFalse -import org.junit.Before -import org.junit.Ignore -import org.junit.Test - -abstract class RotateTwoLaunchedAppsTransition( - testSpec: FlickerTestParameter -) : AppPairsTransition(testSpec) { - override val nonResizeableApp: SplitScreenHelper? - get() = null - - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { - test { - device.wakeUpAndGoToHomeScreen() - this.setRotation(Surface.ROTATION_0) - primaryApp.launchViaIntent(wmHelper) - secondaryApp.launchViaIntent(wmHelper) - updateTasksId() - } - } - teardown { - eachRun { - executeShellCommand(composePairsCommand( - primaryTaskId, secondaryTaskId, pair = false)) - primaryApp.exit(wmHelper) - secondaryApp.exit(wmHelper) - } - } - } - - @Before - override fun setup() { - // AppPairs hasn't been updated to Shell Transition. There will be conflict on rotation. - assumeFalse(isShellTransitionsEnabled()) - super.setup() - } - - @Ignore - @Test - override fun navBarLayerIsVisible() { - super.navBarLayerIsVisible() - } - - @Ignore - @Test - override fun navBarLayerRotatesAndScales() { - super.navBarLayerRotatesAndScales() - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt index e9d438a569d5..5a364ddf3a8f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt @@ -39,7 +39,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( ) { private val mediaSessionManager: MediaSessionManager get() = context.getSystemService(MediaSessionManager::class.java) - ?: error("Could not get MediaSessionManager") + ?: error("Could not get MediaSessionManager") private val mediaController: MediaController? get() = mediaSessionManager.getActiveSessions(null).firstOrNull { @@ -69,8 +69,10 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( action: String? = null, stringExtras: Map<String, String> ) { - launchViaIntentAndWaitShown(wmHelper, expectedWindowName, action, stringExtras, - waitConditions = arrayOf(WindowManagerStateHelper.pipShownCondition)) + launchViaIntentAndWaitShown( + wmHelper, expectedWindowName, action, stringExtras, + waitConditions = arrayOf(WindowManagerStateHelper.pipShownCondition) + ) } /** @@ -85,7 +87,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( // from "the bottom". repeat(FOCUS_ATTEMPTS) { uiDevice.findObject(selector)?.apply { if (isFocusedOrHasFocusedChild) return true } - ?: error("The object we try to focus on is gone.") + ?: error("The object we try to focus on is gone.") uiDevice.pressDPadDown() uiDevice.waitForIdle() @@ -100,29 +102,62 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( // Wait on WMHelper or simply wait for 3 seconds wmHelper?.waitPipShown() ?: SystemClock.sleep(3_000) // when entering pip, the dismiss button is visible at the start. to ensure the pip - // animation is complete, wait until the pip dismiss button is no longer visible. + // animation is complete, wait until the pip dismiss button is no longer visible. // b/176822698: dismiss-only state will be removed in the future uiDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "dismiss")), FIND_TIMEOUT) } + @JvmOverloads + fun enterPipViaHomeButton(wmHelper: WindowManagerStateHelper) { + uiDevice.pressHome() + + // Wait on WMHelper or simply wait for 3 seconds + wmHelper.waitPipShown() ?: SystemClock.sleep(3_000) + // when entering pip, the dismiss button is visible at the start. to ensure the pip + // animation is complete, wait until the pip dismiss button is no longer visible. + // b/176822698: dismiss-only state will be removed in the future + uiDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "dismiss")), FIND_TIMEOUT) + } + + @JvmOverloads + fun enterPipViaSwipeToHome(wmHelper: WindowManagerStateHelper) { + uiDevice.swipe( + uiDevice.displayWidth / 2, uiDevice.displayHeight - 10, + uiDevice.displayWidth / 2, uiDevice.displayHeight - 300, 3 + ) + + // Wait on WMHelper or simply wait for 3 seconds + wmHelper.waitPipShown() ?: SystemClock.sleep(3_000) + // when entering pip, the dismiss button is visible at the start. to ensure the pip + // animation is complete, wait until the pip dismiss button is no longer visible. + // b/176822698: dismiss-only state will be removed in the future + uiDevice.wait(Until.gone(By.res(SYSTEMUI_PACKAGE, "dismiss")), FIND_TIMEOUT) + } + + fun enableEnterPipOnUserLeaveHint() { + clickObject(ENTER_PIP_ON_USER_LEAVE_HINT) + } + fun clickStartMediaSessionButton() { clickObject(MEDIA_SESSION_START_RADIO_BUTTON_ID) } fun checkWithCustomActionsCheckbox() = uiDevice - .findObject(By.res(component.packageName, WITH_CUSTOM_ACTIONS_BUTTON_ID)) - ?.takeIf { it.isCheckable } - ?.apply { if (!isChecked) clickObject(WITH_CUSTOM_ACTIONS_BUTTON_ID) } - ?: error("'With custom actions' checkbox not found") + .findObject(By.res(component.packageName, WITH_CUSTOM_ACTIONS_BUTTON_ID)) + ?.takeIf { it.isCheckable } + ?.apply { if (!isChecked) clickObject(WITH_CUSTOM_ACTIONS_BUTTON_ID) } + ?: error("'With custom actions' checkbox not found") fun pauseMedia() = mediaController?.transportControls?.pause() - ?: error("No active media session found") + ?: error("No active media session found") fun stopMedia() = mediaController?.transportControls?.stop() - ?: error("No active media session found") + ?: error("No active media session found") - @Deprecated("Use PipAppHelper.closePipWindow(wmHelper) instead", - ReplaceWith("closePipWindow(wmHelper)")) + @Deprecated( + "Use PipAppHelper.closePipWindow(wmHelper) instead", + ReplaceWith("closePipWindow(wmHelper)") + ) fun closePipWindow() { if (isTelevision) { uiDevice.closeTvPipWindow() @@ -152,7 +187,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss") uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT) val dismissPipObject = uiDevice.findObject(dismissSelector) - ?: error("PIP window dismiss button not found") + ?: error("PIP window dismiss button not found") val dismissButtonBounds = dismissPipObject.visibleBounds uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY()) } @@ -172,7 +207,7 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( val expandSelector = By.res(SYSTEMUI_PACKAGE, "expand_button") uiDevice.wait(Until.hasObject(expandSelector), FIND_TIMEOUT) val expandPipObject = uiDevice.findObject(expandSelector) - ?: error("PIP window expand button not found") + ?: error("PIP window expand button not found") val expandButtonBounds = expandPipObject.visibleBounds uiDevice.click(expandButtonBounds.centerX(), expandButtonBounds.centerY()) wmHelper.waitPipGone() @@ -194,5 +229,6 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( private const val ENTER_PIP_BUTTON_ID = "enter_pip" private const val WITH_CUSTOM_ACTIONS_BUTTON_ID = "with_custom_actions" private const val MEDIA_SESSION_START_RADIO_BUTTON_ID = "media_session_start" + private const val ENTER_PIP_ON_USER_LEAVE_HINT = "enter_pip_on_leave_manual" } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt index 0ec9b2d869a8..b902e5d1b454 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt @@ -17,7 +17,6 @@ package com.android.wm.shell.flicker.helpers import android.app.Instrumentation -import android.content.res.Resources import com.android.server.wm.traces.common.FlickerComponentName import com.android.server.wm.traces.parser.toFlickerComponent import com.android.wm.shell.flicker.testapp.Components @@ -32,11 +31,6 @@ class SplitScreenHelper( const val TEST_REPETITIONS = 1 const val TIMEOUT_MS = 3_000L - // TODO: remove all legacy split screen flicker tests when legacy split screen is fully - // deprecated. - fun isUsingLegacySplit(): Boolean = - Resources.getSystem().getBoolean(com.android.internal.R.bool.config_useLegacySplit) - fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper = SplitScreenHelper(instrumentation, Components.SplitScreenActivity.LABEL, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt deleted file mode 100644 index c86a1229d8d8..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import android.view.WindowManagerPolicyConstants -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group4 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible -import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open activity and dock to primary split screen - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenDockActivity` - */ -@Presubmit -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group4 -class EnterSplitScreenDockActivity( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - device.launchSplitScreen(wmHelper) - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(LAUNCHER_COMPONENT, LIVE_WALLPAPER_COMPONENT, - splitScreenApp.component, FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT, LAUNCHER_COMPONENT) - - @Presubmit - @Test - fun dockedStackPrimaryBoundsIsVisibleAtEnd() = - testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation, - splitScreenApp.component) - - @Presubmit - @Test - fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible() - - @Presubmit - @Test - fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() - - @Presubmit - @Test - fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() - - @Presubmit - @Test - fun appWindowIsVisible() { - testSpec.assertWmEnd { - isAppWindowVisible(splitScreenApp.component) - } - } - - @FlakyTest - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0), // bugId = 179116910 - supportedNavigationModes = listOf( - WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY) - ) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt deleted file mode 100644 index 2f9244be9c18..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenFromDetachedRecentTask.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group4 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test enter split screen from a detached recent task - * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenFromDetachedRecentTask` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@Group4 -class EnterSplitScreenFromDetachedRecentTask( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - - override val transition: FlickerBuilder.() -> Unit - get() = { - cleanSetup(this) - setup { - eachRun { - splitScreenApp.launchViaIntent(wmHelper) - // Press back to remove the task, but it should still be shown in recent. - device.pressBack() - } - } - transitions { - device.launchSplitScreen(wmHelper) - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(LAUNCHER_COMPONENT, - FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT, - splitScreenApp.component) - - @Presubmit - @Test - fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd() - - @Presubmit - @Test - fun appWindowIsVisible() { - testSpec.assertWmEnd { - isAppWindowVisible(splitScreenApp.component) - } - } - - @Presubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910 - ) - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt deleted file mode 100644 index 1740c3ec24ca..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group4 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible -import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open activity to primary split screen and dock secondary activity to side - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenLaunchToSide` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group4 -class EnterSplitScreenLaunchToSide( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - device.launchSplitScreen(wmHelper) - device.reopenAppFromOverview(wmHelper) - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component, - secondaryApp.component, FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT) - - @Presubmit - @Test - fun dockedStackPrimaryBoundsIsVisibleAtEnd() = - testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation, - splitScreenApp.component) - - @Presubmit - @Test - fun dockedStackSecondaryBoundsIsVisibleAtEnd() = - testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation, - secondaryApp.component) - - @Presubmit - @Test - fun dockedStackDividerBecomesVisible() = testSpec.dockedStackDividerBecomesVisible() - - @Presubmit - @Test - fun appWindowBecomesVisible() { - testSpec.assertWm { - // when the app is launched, first the activity becomes visible, then the - // SnapshotStartingWindow appears and then the app window becomes visible. - // Because we log WM once per frame, sometimes the activity and the window - // become visible in the same entry, sometimes not, thus it is not possible to - // assert the visibility of the activity here - this.isAppWindowInvisible(secondaryApp.component) - .then() - // during re-parenting, the window may disappear and reappear from the - // trace, this occurs because we log only 1x per frame - .notContains(secondaryApp.component, isOptional = true) - .then() - .isAppWindowVisible(secondaryApp.component) - } - } - - @Presubmit - @Test - fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() - - @Presubmit - @Test - fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0) // bugId = 175687842 - ) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt deleted file mode 100644 index 4c063b918e96..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNotSupportNonResizable.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group4 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.canSplitScreen -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.After -import org.junit.Assert -import org.junit.Before -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test enter split screen from non-resizable activity. When the device doesn't support - * non-resizable in multi window, there should be no button to enter split screen for non-resizable - * activity. - * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenNotSupportNonResizable` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@Group4 -class EnterSplitScreenNotSupportNonResizable( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - - override val transition: FlickerBuilder.() -> Unit - get() = { - cleanSetup(this) - setup { - eachRun { - nonResizeableApp.launchViaIntent(wmHelper) - } - } - transitions { - if (device.canSplitScreen(wmHelper)) { - Assert.fail("Non-resizeable app should not enter split screen") - } - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(LAUNCHER_COMPONENT, - FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT, - nonResizeableApp.component, - splitScreenApp.component) - - @Before - override fun setup() { - super.setup() - setSupportsNonResizableMultiWindow(instrumentation, -1) - } - - @After - override fun teardown() { - super.teardown() - resetMultiWindowConfig(instrumentation) - } - - @Presubmit - @Test - fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt deleted file mode 100644 index f75dee619564..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenSupportNonResizable.kt +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.After -import org.junit.Before -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test enter split screen from non-resizable activity. When the device supports - * non-resizable in multi window, there should be a button to enter split screen for non-resizable - * activity. - * - * To run this test: `atest WMShellFlickerTests:EnterSplitScreenSupportNonResizable` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@Group2 -class EnterSplitScreenSupportNonResizable( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - - override val transition: FlickerBuilder.() -> Unit - get() = { - cleanSetup(this) - setup { - eachRun { - nonResizeableApp.launchViaIntent(wmHelper) - } - } - transitions { - device.launchSplitScreen(wmHelper) - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(LAUNCHER_COMPONENT, - FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT, - nonResizeableApp.component, - splitScreenApp.component) - - @Before - override fun setup() { - super.setup() - setSupportsNonResizableMultiWindow(instrumentation, 1) - } - - @After - override fun teardown() { - super.teardown() - resetMultiWindowConfig(instrumentation) - } - - @Presubmit - @Test - fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd() - - @Presubmit - @Test - fun appWindowIsVisible() { - testSpec.assertWmEnd { - isAppWindowVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt deleted file mode 100644 index ef7d65e8a732..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.legacysplitscreen - -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.exitSplitScreenFromBottom -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open resizeable activity split in primary, and drag divider to bottom exit split screen - * To run this test: `atest WMShellFlickerTests:ExitLegacySplitScreenFromBottom` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class ExitLegacySplitScreenFromBottom( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - setup { - eachRun { - splitScreenApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - } - } - teardown { - eachRun { - splitScreenApp.exit(wmHelper) - } - } - transitions { - device.exitSplitScreenFromBottom(wmHelper) - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN, - splitScreenApp.component, secondaryApp.component, - FlickerComponentName.SNAPSHOT) - - @FlakyTest - @Test - fun layerBecomesInvisible() { - testSpec.assertLayers { - this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) - .then() - .isInvisible(DOCKED_STACK_DIVIDER_COMPONENT) - } - } - - @FlakyTest - @Test - fun appWindowBecomesInVisible() { - testSpec.assertWm { - this.isAppWindowVisible(secondaryApp.component) - .then() - .isAppWindowInvisible(secondaryApp.component) - } - } - - @FlakyTest - @Test - fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() - - @FlakyTest - @Test - fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() - - @FlakyTest - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - @FlakyTest - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0) // b/175687842 - ) - } - } -}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt deleted file mode 100644 index d913a6d85d3d..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test dock activity to primary split screen, and open secondary to side, exit primary split - * and test secondary activity become full screen. - * To run this test: `atest WMShellFlickerTests:ExitPrimarySplitScreenShowSecondaryFullscreen` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class ExitPrimarySplitScreenShowSecondaryFullscreen( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - teardown { - eachRun { - secondaryApp.exit(wmHelper) - } - } - transitions { - splitScreenApp.launchViaIntent(wmHelper) - secondaryApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - device.reopenAppFromOverview(wmHelper) - // TODO(b/175687842) Can not find Split screen divider, use exit() instead - splitScreenApp.exit(wmHelper) - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN, - splitScreenApp.component, secondaryApp.component, - FlickerComponentName.SNAPSHOT) - - @Presubmit - @Test - fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd() - - @FlakyTest - @Test - fun layerBecomesInvisible() { - testSpec.assertLayers { - this.isVisible(splitScreenApp.component) - .then() - .isInvisible(splitScreenApp.component) - } - } - - @FlakyTest - @Test - fun appWindowBecomesInVisible() { - testSpec.assertWm { - this.isAppWindowVisible(splitScreenApp.component) - .then() - .isAppWindowInvisible(splitScreenApp.component) - } - } - - @Presubmit - @Test - fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() - - @Presubmit - @Test - fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() - - @Presubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910 - ) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt deleted file mode 100644 index f3ff7b156aaf..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentNotSupportNonResizable.kt +++ /dev/null @@ -1,196 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT -import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.After -import org.junit.Before -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test launch non-resizable activity via intent in split screen mode. When the device does not - * support non-resizable in multi window, it should trigger exit split screen. - * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromIntentNotSupportNonResizable` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class LegacySplitScreenFromIntentNotSupportNonResizable( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - - override val transition: FlickerBuilder.() -> Unit - get() = { - cleanSetup(this) - setup { - eachRun { - splitScreenApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - } - } - transitions { - nonResizeableApp.launchViaIntent(wmHelper) - wmHelper.waitForAppTransitionIdle() - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT, - nonResizeableApp.component, splitScreenApp.component, - FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT) - - @Before - override fun setup() { - super.setup() - setSupportsNonResizableMultiWindow(instrumentation, -1) - } - - @After - override fun teardown() { - super.teardown() - resetMultiWindowConfig(instrumentation) - } - - @Presubmit - @Test - fun resizableAppLayerBecomesInvisible() { - testSpec.assertLayers { - this.isVisible(splitScreenApp.component) - .then() - .isInvisible(splitScreenApp.component) - } - } - - @Presubmit - @Test - fun nonResizableAppLayerBecomesVisible() { - testSpec.assertLayers { - this.notContains(nonResizeableApp.component) - .then() - .isInvisible(nonResizeableApp.component) - .then() - .isVisible(nonResizeableApp.component) - } - } - - /** - * Assets that [splitScreenApp] exists at the start of the trace and, once it becomes - * invisible, it remains invisible until the end of the trace. - */ - @Presubmit - @Test - fun resizableAppWindowBecomesInvisible() { - testSpec.assertWm { - // when the activity gets PAUSED the window may still be marked as visible - // it will be updated in the next log entry. This occurs because we record 1x - // per frame, thus ignore activity check here - this.isAppWindowVisible(splitScreenApp.component) - .then() - // immediately after the window (after onResume and before perform relayout) - // the activity is invisible. This may or not be logged, since we record 1x - // per frame, thus ignore activity check here - .isAppWindowInvisible(splitScreenApp.component) - } - } - - /** - * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then - * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes - * visible, it remains visible until the end of the trace. - */ - @Presubmit - @Test - fun nonResizableAppWindowBecomesVisible() { - testSpec.assertWm { - this.notContains(nonResizeableApp.component) - .then() - // we log once per frame, upon logging, window may be visible or not depending - // on what was processed until that moment. Both behaviors are correct - .isAppWindowInvisible(nonResizeableApp.component, isOptional = true) - .then() - // immediately after the window (after onResume and before perform relayout) - // the activity is invisible. This may or not be logged, since we record 1x - // per frame, thus ignore activity check here - .isAppWindowVisible(nonResizeableApp.component) - } - } - - /** - * Asserts that both the app window and the activity are visible at the end of the trace - */ - @Presubmit - @Test - fun nonResizableAppWindowBecomesVisibleAtEnd() { - testSpec.assertWmEnd { - isAppWindowVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd() - - @Presubmit - @Test - fun onlyNonResizableAppWindowIsVisibleAtEnd() { - testSpec.assertWmEnd { - isAppWindowInvisible(splitScreenApp.component) - isAppWindowVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt deleted file mode 100644 index 42e707ab0850..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromIntentSupportNonResizable.kt +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT -import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.After -import org.junit.Before -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test launch non-resizable activity via intent in split screen mode. When the device supports - * non-resizable in multi window, it should show the non-resizable app in split screen. - * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromIntentSupportNonResizable` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class LegacySplitScreenFromIntentSupportNonResizable( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - - override val transition: FlickerBuilder.() -> Unit - get() = { - cleanSetup(this) - setup { - eachRun { - splitScreenApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - } - } - transitions { - nonResizeableApp.launchViaIntent(wmHelper) - wmHelper.waitForAppTransitionIdle() - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT, - nonResizeableApp.component, splitScreenApp.component, - FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT) - - @Before - override fun setup() { - super.setup() - setSupportsNonResizableMultiWindow(instrumentation, 1) - } - - @After - override fun teardown() { - super.teardown() - resetMultiWindowConfig(instrumentation) - } - - @Presubmit - @Test - fun nonResizableAppLayerBecomesVisible() { - testSpec.assertLayers { - this.isInvisible(nonResizeableApp.component) - .then() - .isVisible(nonResizeableApp.component) - } - } - - /** - * Assets that [nonResizeableApp] doesn't exist at the start of the trace, then - * [nonResizeableApp] is created (visible or not) and, once [nonResizeableApp] becomes - * visible, it remains visible until the end of the trace. - */ - @Presubmit - @Test - fun nonResizableAppWindowBecomesVisible() { - testSpec.assertWm { - this.notContains(nonResizeableApp.component) - .then() - // we log once per frame, upon logging, window may be visible or not depending - // on what was processed until that moment. Both behaviors are correct - .isAppWindowInvisible(nonResizeableApp.component, isOptional = true) - .then() - // immediately after the window (after onResume and before perform relayout) - // the activity is invisible. This may or not be logged, since we record 1x - // per frame, thus ignore activity check here - .isAppWindowVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd() - - @Presubmit - @Test - fun bothAppsWindowsAreVisibleAtEnd() { - testSpec.assertWmEnd { - isAppWindowVisible(splitScreenApp.component) - isAppWindowVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt deleted file mode 100644 index c1fba7d1530c..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentNotSupportNonResizable.kt +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT -import com.android.wm.shell.flicker.dockedStackDividerNotExistsAtEnd -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.After -import org.junit.Before -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test launch non-resizable activity via recent overview in split screen mode. When the device does - * not support non-resizable in multi window, it should trigger exit split screen. - * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromRecentNotSupportNonResizable` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class LegacySplitScreenFromRecentNotSupportNonResizable( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - - override val transition: FlickerBuilder.() -> Unit - get() = { - cleanSetup(this) - setup { - eachRun { - nonResizeableApp.launchViaIntent(wmHelper) - splitScreenApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - } - } - transitions { - device.reopenAppFromOverview(wmHelper) - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT, - TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component, - FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT) - - @Before - override fun setup() { - super.setup() - setSupportsNonResizableMultiWindow(instrumentation, -1) - } - - @After - override fun teardown() { - super.teardown() - resetMultiWindowConfig(instrumentation) - } - - @Presubmit - @Test - fun resizableAppLayerBecomesInvisible() { - testSpec.assertLayers { - this.isVisible(splitScreenApp.component) - .then() - .isInvisible(splitScreenApp.component) - } - } - - @Presubmit - @Test - fun nonResizableAppLayerBecomesVisible() { - testSpec.assertLayers { - this.isInvisible(nonResizeableApp.component) - .then() - .isVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - fun resizableAppWindowBecomesInvisible() { - testSpec.assertWm { - // when the activity gets PAUSED the window may still be marked as visible - // it will be updated in the next log entry. This occurs because we record 1x - // per frame, thus ignore activity check here - this.isAppWindowVisible(splitScreenApp.component) - .then() - // immediately after the window (after onResume and before perform relayout) - // the activity is invisible. This may or not be logged, since we record 1x - // per frame, thus ignore activity check here - .isAppWindowInvisible(splitScreenApp.component) - } - } - - @FlakyTest - @Test - fun nonResizableAppWindowBecomesVisible() { - testSpec.assertWm { - this.isAppWindowInvisible(nonResizeableApp.component) - .then() - .isAppWindowVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - fun dockedStackDividerNotExistsAtEnd() = testSpec.dockedStackDividerNotExistsAtEnd() - - @Presubmit - @Test - fun onlyNonResizableAppWindowIsVisibleAtEnd() { - testSpec.assertWmEnd { - isAppWindowInvisible(splitScreenApp.component) - isAppWindowVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt deleted file mode 100644 index 6ac8683ac054..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenFromRecentSupportNonResizable.kt +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT -import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.resetMultiWindowConfig -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setSupportsNonResizableMultiWindow -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.After -import org.junit.Before -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test launch non-resizable activity via recent overview in split screen mode. When the device - * supports non-resizable in multi window, it should show the non-resizable app in split screen. - * To run this test: `atest WMShellFlickerTests:LegacySplitScreenFromRecentSupportNonResizable` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class LegacySplitScreenFromRecentSupportNonResizable( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - - override val transition: FlickerBuilder.() -> Unit - get() = { - cleanSetup(this) - setup { - eachRun { - nonResizeableApp.launchViaIntent(wmHelper) - splitScreenApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - } - } - transitions { - device.reopenAppFromOverview(wmHelper) - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(DOCKED_STACK_DIVIDER_COMPONENT, LAUNCHER_COMPONENT, LETTERBOX_COMPONENT, - TOAST_COMPONENT, splitScreenApp.component, nonResizeableApp.component, - FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT) - - @Before - override fun setup() { - super.setup() - setSupportsNonResizableMultiWindow(instrumentation, 1) - } - - @After - override fun teardown() { - super.teardown() - resetMultiWindowConfig(instrumentation) - } - - @Presubmit - @Test - fun nonResizableAppLayerBecomesVisible() { - testSpec.assertLayers { - this.isInvisible(nonResizeableApp.component) - .then() - .isVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - fun nonResizableAppWindowBecomesVisible() { - testSpec.assertWm { - // when the app is launched, first the activity becomes visible, then the - // SnapshotStartingWindow appears and then the app window becomes visible. - // Because we log WM once per frame, sometimes the activity and the window - // become visible in the same entry, sometimes not, thus it is not possible to - // assert the visibility of the activity here - this.isAppWindowInvisible(nonResizeableApp.component) - .then() - // during re-parenting, the window may disappear and reappear from the - // trace, this occurs because we log only 1x per frame - .notContains(nonResizeableApp.component, isOptional = true) - .then() - // if the window reappears after re-parenting it will most likely not - // be visible in the first log entry (because we log only 1x per frame) - .isAppWindowInvisible(nonResizeableApp.component, isOptional = true) - .then() - .isAppWindowVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd() - - @Presubmit - @Test - fun bothAppsWindowsAreVisibleAtEnd() { - testSpec.assertWmEnd { - isAppWindowVisible(splitScreenApp.component) - isAppWindowVisible(nonResizeableApp.component) - } - } - - @Presubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt deleted file mode 100644 index b01f41c9e2ec..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2021 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.wm.shell.flicker.legacysplitscreen - -import android.view.Surface -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen - -abstract class LegacySplitScreenRotateTransition( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { - eachRun { - device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) - secondaryApp.launchViaIntent(wmHelper) - splitScreenApp.launchViaIntent(wmHelper) - } - } - teardown { - eachRun { - splitScreenApp.exit(wmHelper) - secondaryApp.exit(wmHelper) - this.setRotation(Surface.ROTATION_0) - } - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt deleted file mode 100644 index fb1004bda0cb..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.entireScreenCovered -import com.android.server.wm.flicker.helpers.exitSplitScreen -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.navBarLayerIsVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarLayerIsVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible -import com.android.wm.shell.flicker.helpers.SimpleAppHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open app to split screen. - * To run this test: `atest WMShellFlickerTests:LegacySplitScreenToLauncher` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class LegacySplitScreenToLauncher( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - private val testApp = SimpleAppHelper(instrumentation) - - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { - test { - device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) - } - eachRun { - testApp.launchViaIntent(wmHelper) - this.setRotation(testSpec.endRotation) - device.launchSplitScreen(wmHelper) - device.waitForIdle() - } - } - teardown { - eachRun { - testApp.exit(wmHelper) - } - } - transitions { - device.exitSplitScreen() - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(LAUNCHER_COMPONENT, FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT) - - @Presubmit - @Test - fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() - - @Presubmit - @Test - fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() - - @Presubmit - @Test - fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible() - - @Presubmit - @Test - fun entireScreenCovered() = testSpec.entireScreenCovered() - - @Presubmit - @Test - fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales() - - @FlakyTest(bugId = 206753786) - @Test - fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales() - - @Presubmit - @Test - fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible() - - @FlakyTest - @Test - fun dockedStackDividerBecomesInvisible() = testSpec.dockedStackDividerBecomesInvisible() - - @FlakyTest - @Test - fun layerBecomesInvisible() { - testSpec.assertLayers { - this.isVisible(testApp.component) - .then() - .isInvisible(testApp.component) - } - } - - @FlakyTest - @Test - fun focusDoesNotChange() { - testSpec.assertEventLog { - this.focusDoesNotChange() - } - } - - @Presubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - // b/161435597 causes the test not to work on 90 degrees - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt deleted file mode 100644 index a4a1f617e497..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2020 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/LICENSE2.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.wm.shell.flicker.legacysplitscreen - -import android.app.Instrumentation -import android.content.Context -import android.support.test.launcherhelper.LauncherStrategyFactory -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerBuilderProvider -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.getDevEnableNonResizableMultiWindow -import com.android.wm.shell.flicker.helpers.MultiWindowHelper.Companion.setDevEnableNonResizableMultiWindow -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.After -import org.junit.Assume.assumeFalse -import org.junit.Assume.assumeTrue -import org.junit.Before -import org.junit.Test - -abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) { - protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - protected val context: Context = instrumentation.context - protected val splitScreenApp = SplitScreenHelper.getPrimary(instrumentation) - protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) - protected val nonResizeableApp = SplitScreenHelper.getNonResizeable(instrumentation) - protected val LAUNCHER_COMPONENT = FlickerComponentName("", - LauncherStrategyFactory.getInstance(instrumentation) - .launcherStrategy.supportedLauncherPackage) - private var prevDevEnableNonResizableMultiWindow = 0 - - @Before - open fun setup() { - // Only run legacy split tests when the system is using legacy split screen. - assumeTrue(SplitScreenHelper.isUsingLegacySplit()) - // Legacy split is having some issue with Shell transition, and will be deprecated soon. - assumeFalse(isShellTransitionsEnabled()) - prevDevEnableNonResizableMultiWindow = getDevEnableNonResizableMultiWindow(context) - if (prevDevEnableNonResizableMultiWindow != 0) { - // Turn off the development option - setDevEnableNonResizableMultiWindow(context, 0) - } - } - - @After - open fun teardown() { - setDevEnableNonResizableMultiWindow(context, prevDevEnableNonResizableMultiWindow) - } - - /** - * List of windows that are ignored when verifying that visible elements appear on 2 - * consecutive entries in the trace. - * - * b/182720234 - */ - open val ignoredWindows: List<FlickerComponentName> = listOf( - FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT) - - protected open val transition: FlickerBuilder.() -> Unit - get() = { - setup { - eachRun { - device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) - secondaryApp.launchViaIntent(wmHelper) - splitScreenApp.launchViaIntent(wmHelper) - this.setRotation(testSpec.startRotation) - } - } - teardown { - eachRun { - secondaryApp.exit(wmHelper) - splitScreenApp.exit(wmHelper) - this.setRotation(Surface.ROTATION_0) - } - } - } - - @FlickerBuilderProvider - fun buildFlicker(): FlickerBuilder { - return FlickerBuilder(instrumentation).apply { - transition(this) - } - } - - internal open val cleanSetup: FlickerBuilder.() -> Unit - get() = { - setup { - eachRun { - device.wakeUpAndGoToHomeScreen() - device.openQuickStepAndClearRecentAppsFromOverview(wmHelper) - this.setRotation(testSpec.startRotation) - } - } - teardown { - eachRun { - nonResizeableApp.exit(wmHelper) - splitScreenApp.exit(wmHelper) - device.pressHome() - this.setRotation(Surface.ROTATION_0) - } - } - } - - @FlakyTest(bugId = 178447631) - @Test - open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { - testSpec.assertWm { - this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoredWindows) - } - } - - @FlakyTest(bugId = 178447631) - @Test - open fun visibleLayersShownMoreThanOneConsecutiveEntry() { - testSpec.assertLayers { - this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoredWindows) - } - } - - companion object { - internal val LIVE_WALLPAPER_COMPONENT = FlickerComponentName("", - "com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2") - internal val LETTERBOX_COMPONENT = FlickerComponentName("", "Letterbox") - internal val TOAST_COMPONENT = FlickerComponentName("", "Toast") - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS deleted file mode 100644 index 8446b37dbf06..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -# window manager > wm shell > Split Screen -# Bug component: 928697 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt deleted file mode 100644 index 087b21c544c5..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.entireScreenCovered -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.statusBarLayerIsVisible -import com.android.server.wm.traces.common.FlickerComponentName -import com.android.wm.shell.flicker.appPairsDividerBecomesVisible -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open app to split screen. - * To run this test: `atest WMShellFlickerTests:OpenAppToLegacySplitScreen` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class OpenAppToLegacySplitScreen( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - device.launchSplitScreen(wmHelper) - wmHelper.waitForAppTransitionIdle() - } - } - - override val ignoredWindows: List<FlickerComponentName> - get() = listOf(LAUNCHER_COMPONENT, splitScreenApp.component, - FlickerComponentName.SPLASH_SCREEN, - FlickerComponentName.SNAPSHOT) - - @FlakyTest - @Test - fun appWindowBecomesVisible() { - testSpec.assertWm { - this.isAppWindowInvisible(splitScreenApp.component) - .then() - .isAppWindowVisible(splitScreenApp.component) - } - } - - @Presubmit - @Test - fun entireScreenCovered() = testSpec.entireScreenCovered() - - @Presubmit - @Test - fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible() - - @Presubmit - @Test - fun appPairsDividerBecomesVisible() = testSpec.appPairsDividerBecomesVisible() - - @FlakyTest - @Test - fun layerBecomesVisible() { - testSpec.assertLayers { - this.isInvisible(splitScreenApp.component) - .then() - .isVisible(splitScreenApp.component) - } - } - - @Presubmit - @Test - fun focusChanges() { - testSpec.assertEventLog { - this.focusChanges(splitScreenApp.`package`, - "recents_animation_input_consumer", "NexusLauncherActivity") - } - } - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0) // bugId = 179116910 - ) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt deleted file mode 100644 index e2da1a4565c0..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.legacysplitscreen - -import android.util.Rational -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import androidx.test.uiautomator.By -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.entireScreenCovered -import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.resizeSplitScreen -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.navBarLayerIsVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarLayerIsVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.server.wm.traces.common.region.Region -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER_COMPONENT -import com.android.wm.shell.flicker.helpers.SimpleAppHelper -import com.android.wm.shell.flicker.testapp.Components -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test split screen resizing window transitions. - * To run this test: `atest WMShellFlickerTests:ResizeLegacySplitScreen` - * - * Currently it runs only in 0 degrees because of b/156100803 - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@FlakyTest(bugId = 159096424) -@Group2 -class ResizeLegacySplitScreen( - testSpec: FlickerTestParameter -) : LegacySplitScreenTransition(testSpec) { - private val testAppTop = SimpleAppHelper(instrumentation) - private val testAppBottom = ImeAppHelper(instrumentation) - - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { - eachRun { - device.wakeUpAndGoToHomeScreen() - this.setRotation(testSpec.startRotation) - this.launcherStrategy.clearRecentAppsFromOverview() - testAppBottom.launchViaIntent(wmHelper) - device.pressHome() - testAppTop.launchViaIntent(wmHelper) - device.waitForIdle() - device.launchSplitScreen(wmHelper) - val snapshot = - device.findObject(By.res(device.launcherPackageName, "snapshot")) - snapshot.click() - testAppBottom.openIME(device) - device.pressBack() - device.resizeSplitScreen(startRatio) - } - } - teardown { - eachRun { - testAppTop.exit(wmHelper) - testAppBottom.exit(wmHelper) - } - } - transitions { - device.resizeSplitScreen(stopRatio) - } - } - - @Test - fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() - - @Test - fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() - - @FlakyTest(bugId = 156223549) - @Test - fun topAppWindowIsAlwaysVisible() { - testSpec.assertWm { - this.isAppWindowVisible(Components.SimpleActivity.COMPONENT.toFlickerComponent()) - } - } - - @FlakyTest(bugId = 156223549) - @Test - fun bottomAppWindowIsAlwaysVisible() { - testSpec.assertWm { - this.isAppWindowVisible(Components.ImeActivity.COMPONENT.toFlickerComponent()) - } - } - - @Test - fun navBarLayerIsVisible() = testSpec.navBarLayerIsVisible() - - @Test - fun statusBarLayerIsVisible() = testSpec.statusBarLayerIsVisible() - - @Test - fun entireScreenCovered() = testSpec.entireScreenCovered() - - @Test - fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales() - - @FlakyTest(bugId = 206753786) - @Test - fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales() - - @Test - fun topAppLayerIsAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(Components.SimpleActivity.COMPONENT.toFlickerComponent()) - } - } - - @Test - fun bottomAppLayerIsAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(Components.ImeActivity.COMPONENT.toFlickerComponent()) - } - } - - @Test - fun dividerLayerIsAlwaysVisible() { - testSpec.assertLayers { - this.isVisible(DOCKED_STACK_DIVIDER_COMPONENT) - } - } - - @FlakyTest - @Test - fun appsStartingBounds() { - testSpec.assertLayersStart { - val displayBounds = WindowUtils.displayBounds - val dividerBounds = - layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds - - val topAppBounds = Region.from(0, 0, dividerBounds.right, - dividerBounds.top + WindowUtils.dockedStackDividerInset) - val bottomAppBounds = Region.from(0, - dividerBounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.right, - displayBounds.bottom - WindowUtils.navigationBarFrameHeight) - visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent()) - .coversExactly(topAppBounds) - visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent()) - .coversExactly(bottomAppBounds) - } - } - - @FlakyTest - @Test - fun appsEndingBounds() { - testSpec.assertLayersStart { - val displayBounds = WindowUtils.displayBounds - val dividerBounds = - layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region.bounds - - val topAppBounds = Region.from(0, 0, dividerBounds.right, - dividerBounds.top + WindowUtils.dockedStackDividerInset) - val bottomAppBounds = Region.from(0, - dividerBounds.bottom - WindowUtils.dockedStackDividerInset, - displayBounds.right, - displayBounds.bottom - WindowUtils.navigationBarFrameHeight) - - visibleRegion(Components.SimpleActivity.COMPONENT.toFlickerComponent()) - .coversExactly(topAppBounds) - visibleRegion(Components.ImeActivity.COMPONENT.toFlickerComponent()) - .coversExactly(bottomAppBounds) - } - } - - @Test - fun focusDoesNotChange() { - testSpec.assertEventLog { - focusDoesNotChange() - } - } - - companion object { - private val startRatio = Rational(1, 3) - private val stopRatio = Rational(2, 3) - - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0)) - .map { - val description = (startRatio.toString().replace("/", "-") + "_to_" + - stopRatio.toString().replace("/", "-")) - val newName = "${FlickerTestParameter.defaultName(it)}_$description" - FlickerTestParameter(it.config, nameOverride = newName) - } - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt deleted file mode 100644 index d703ea082c87..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppAndEnterSplitScreen.kt +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test dock activity to primary split screen and rotate - * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppAndEnterSplitScreen` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class RotateOneLaunchedAppAndEnterSplitScreen( - testSpec: FlickerTestParameter -) : LegacySplitScreenRotateTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - device.launchSplitScreen(wmHelper) - this.setRotation(testSpec.startRotation) - } - } - - @Presubmit - @Test - fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd() - - @Presubmit - @Test - fun dockedStackPrimaryBoundsIsVisibleAtEnd() = - testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation, - splitScreenApp.component) - - @Presubmit - @Test - fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales() - - @FlakyTest(bugId = 206753786) - @Test - fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales() - - @Presubmit - @Test - fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() - - @Presubmit - @Test - fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() - - @FlakyTest - @Test - fun appWindowBecomesVisible() { - testSpec.assertWm { - this.isAppWindowInvisible(splitScreenApp.component) - .then() - .isAppWindowVisible(splitScreenApp.component) - } - } - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt deleted file mode 100644 index 6b1883914e59..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppInSplitScreenMode.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Rotate - * To run this test: `atest WMShellFlickerTests:RotateOneLaunchedAppInSplitScreenMode` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class RotateOneLaunchedAppInSplitScreenMode( - testSpec: FlickerTestParameter -) : LegacySplitScreenRotateTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - this.setRotation(testSpec.startRotation) - device.launchSplitScreen(wmHelper) - } - } - - @Presubmit - @Test - fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd() - - @Presubmit - @Test - fun dockedStackPrimaryBoundsIsVisibleAtEnd() = testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd( - testSpec.startRotation, splitScreenApp.component) - - @Presubmit - @Test - fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales() - - @FlakyTest(bugId = 206753786) - @Test - fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales() - - @Presubmit - @Test - fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() - - @Presubmit - @Test - fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() - - @FlakyTest - @Test - fun appWindowBecomesVisible() { - testSpec.assertWm { - this.isAppWindowInvisible(splitScreenApp.component) - .then() - .isAppWindowVisible(splitScreenApp.component) - } - } - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt deleted file mode 100644 index acd658b5ba56..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppAndEnterSplitScreen.kt +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open app to split screen. - * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppAndEnterSplitScreen` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class RotateTwoLaunchedAppAndEnterSplitScreen( - testSpec: FlickerTestParameter -) : LegacySplitScreenRotateTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - transitions { - this.setRotation(testSpec.startRotation) - device.launchSplitScreen(wmHelper) - device.reopenAppFromOverview(wmHelper) - } - } - - @Presubmit - @Test - fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd() - - @Presubmit - @Test - fun dockedStackPrimaryBoundsIsVisibleAtEnd() = - testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation, - splitScreenApp.component) - - @Presubmit - @Test - fun dockedStackSecondaryBoundsIsVisibleAtEnd() = - testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation, - secondaryApp.component) - - @Presubmit - @Test - fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales() - - @FlakyTest(bugId = 206753786) - @Test - fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales() - - @Presubmit - @Test - fun appWindowBecomesVisible() { - testSpec.assertWm { - // when the app is launched, first the activity becomes visible, then the - // SnapshotStartingWindow appears and then the app window becomes visible. - // Because we log WM once per frame, sometimes the activity and the window - // become visible in the same entry, sometimes not, thus it is not possible to - // assert the visibility of the activity here - this.isAppWindowInvisible(secondaryApp.component) - .then() - // during re-parenting, the window may disappear and reappear from the - // trace, this occurs because we log only 1x per frame - .notContains(secondaryApp.component, isOptional = true) - .then() - // if the window reappears after re-parenting it will most likely not - // be visible in the first log entry (because we log only 1x per frame) - .isAppWindowInvisible(secondaryApp.component, isOptional = true) - .then() - .isAppWindowVisible(secondaryApp.component) - } - } - - @Presubmit - @Test - fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() - - @Presubmit - @Test - fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt deleted file mode 100644 index b40be8b5f401..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppInSplitScreenMode.kt +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.legacysplitscreen - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group2 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.reopenAppFromOverview -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsVisible -import com.android.wm.shell.flicker.dockedStackDividerIsVisibleAtEnd -import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisibleAtEnd -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test open app to split screen. - * To run this test: `atest WMShellFlickerTests:RotateTwoLaunchedAppInSplitScreenMode` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group2 -class RotateTwoLaunchedAppInSplitScreenMode( - testSpec: FlickerTestParameter -) : LegacySplitScreenRotateTransition(testSpec) { - override val transition: FlickerBuilder.() -> Unit - get() = { - super.transition(this) - setup { - eachRun { - device.launchSplitScreen(wmHelper) - device.reopenAppFromOverview(wmHelper) - this.setRotation(testSpec.startRotation) - } - } - transitions { - this.setRotation(testSpec.startRotation) - } - } - - @Presubmit - @Test - fun dockedStackDividerIsVisibleAtEnd() = testSpec.dockedStackDividerIsVisibleAtEnd() - - @Presubmit - @Test - fun dockedStackPrimaryBoundsIsVisibleAtEnd() = - testSpec.dockedStackPrimaryBoundsIsVisibleAtEnd(testSpec.startRotation, - splitScreenApp.component) - - @Presubmit - @Test - fun dockedStackSecondaryBoundsIsVisibleAtEnd() = - testSpec.dockedStackSecondaryBoundsIsVisibleAtEnd(testSpec.startRotation, - secondaryApp.component) - - @Presubmit - @Test - fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales() - - @FlakyTest(bugId = 206753786) - @Test - fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales() - - @FlakyTest - @Test - fun appWindowBecomesVisible() { - testSpec.assertWm { - this.isAppWindowInvisible(secondaryApp.component) - .then() - .isAppWindowVisible(secondaryApp.component) - } - } - - @Presubmit - @Test - fun navBarWindowIsVisible() = testSpec.navBarWindowIsVisible() - - @Presubmit - @Test - fun statusBarWindowIsVisible() = testSpec.statusBarWindowIsVisible() - - @Presubmit - @Test - override fun visibleLayersShownMoreThanOneConsecutiveEntry() = - super.visibleLayersShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - super.visibleWindowsShownMoreThanOneConsecutiveEntry() - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - repetitions = SplitScreenHelper.TEST_REPETITIONS, - supportedRotations = listOf(Surface.ROTATION_0)) // b/178685668 - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt new file mode 100644 index 000000000000..04bf856fd9f8 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2022 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.wm.shell.flicker.pip + +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.annotation.Group3 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import org.junit.Assume +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test entering pip from an app via [onUserLeaveHint] and by navigating to home. + * + * To run this test: `atest WMShellFlickerTests:EnterPipOnUserLeaveHintTest` + * + * Actions: + * Launch an app in full screen + * Select "Via code behind" radio button + * Press Home button to put [pipApp] in pip mode + * + * Notes: + * 1. All assertions are inherited from [EnterPipTest] + * 2. Part of the test setup occurs automatically via + * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * including configuring navigation mode, initial orientation and ensuring no + * apps are running before setup + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group3 +class EnterPipOnUserLeaveHintTest(testSpec: FlickerTestParameter) : EnterPipTest(testSpec) { + + /** + * Defines the transition used to run the test + */ + override val transition: FlickerBuilder.() -> Unit + get() = { + setupAndTeardown(this) + setup { + eachRun { + pipApp.launchViaIntent(wmHelper) + pipApp.enableEnterPipOnUserLeaveHint() + } + } + teardown { + eachRun { + pipApp.exit(wmHelper) + } + } + transitions { + when (testSpec.isGesturalNavigation) { + true -> pipApp.enterPipViaSwipeToHome(wmHelper) + false -> pipApp.enterPipViaHomeButton(wmHelper) + } + } + } + + override fun pipAppLayerAlwaysVisible() { + if (!testSpec.isGesturalNavigation) super.pipAppLayerAlwaysVisible() else { + // pip layer in gesture nav will disappear during transition + testSpec.assertLayers { + this.isVisible(pipApp.component) + .then().isInvisible(pipApp.component) + .then().isVisible(pipApp.component) + } + } + } + + override fun pipLayerReduces() { + // in gestural nav the pip enters through alpha animation + Assume.assumeFalse(testSpec.isGesturalNavigation) + super.pipLayerReduces() + } + + override fun focusChanges() { + // in gestural nav the focus goes to different activity on swipe up + Assume.assumeFalse(testSpec.isGesturalNavigation) + super.focusChanges() + } + + override fun pipLayerRemainInsideVisibleBounds() { + if (!testSpec.isGesturalNavigation) super.pipLayerRemainInsideVisibleBounds() else { + // pip layer in gesture nav will disappear during transition + testSpec.assertLayersStart { + this.visibleRegion(pipApp.component).coversAtMost(displayBounds) + } + testSpec.assertLayersEnd { + this.visibleRegion(pipApp.component).coversAtMost(displayBounds) + } + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index 0640ac526bd0..b4267ffced5c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -43,7 +43,7 @@ import org.junit.runners.Parameterized * * Notes: * 1. Some default assertions (e.g., nav bar, status bar and screen covered) - * are inherited [PipTransition] + * are inherited from [PipTransition] * 2. Part of the test setup occurs automatically via * [com.android.server.wm.flicker.TransitionRunnerWithRules], * including configuring navigation mode, initial orientation and ensuring no @@ -54,7 +54,7 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 -class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { +open class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { /** * Defines the transition used to run the test @@ -98,7 +98,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { */ @Presubmit @Test - fun pipAppLayerAlwaysVisible() { + open fun pipAppLayerAlwaysVisible() { testSpec.assertLayers { this.isVisible(pipApp.component) } @@ -122,7 +122,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { */ @Presubmit @Test - fun pipLayerRemainInsideVisibleBounds() { + open fun pipLayerRemainInsideVisibleBounds() { testSpec.assertLayersVisibleRegion(pipApp.component) { coversAtMost(displayBounds) } @@ -133,7 +133,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { */ @Presubmit @Test - fun pipLayerReduces() { + open fun pipLayerReduces() { val layerName = pipApp.component.toLayerName() testSpec.assertLayers { val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible } @@ -175,7 +175,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { */ @Presubmit @Test - fun focusChanges() { + open fun focusChanges() { testSpec.assertEventLog { this.focusChanges(pipApp.`package`, "NexusLauncherActivity") } @@ -192,8 +192,10 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { @JvmStatic fun getParams(): List<FlickerTestParameter> { return FlickerTestParameterFactory.getInstance() - .getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0), - repetitions = 3) + .getConfigNonRotationTests( + supportedRotations = listOf(Surface.ROTATION_0), + repetitions = 3 + ) } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt index 37e9344348d9..c6a705dacb8d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt @@ -80,7 +80,17 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit /** {@inheritDoc} */ @FlakyTest(bugId = 206753786) @Test - override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() + override fun statusBarLayerRotatesScales() { + Assume.assumeFalse(isShellTransitionsEnabled) + super.statusBarLayerRotatesScales() + } + + @Presubmit + @Test + fun statusBarLayerRotatesScales_ShellTransit() { + Assume.assumeTrue(isShellTransitionsEnabled) + super.statusBarLayerRotatesScales() + } /** {@inheritDoc} */ @FlakyTest(bugId = 197726610) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt index 1a21d32f568c..fe51228230cb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTestShellTransit.kt @@ -16,7 +16,7 @@ package com.android.wm.shell.flicker.pip -import androidx.test.filters.FlakyTest +import android.platform.test.annotations.Presubmit import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -35,7 +35,6 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group4 -@FlakyTest(bugId = 217777115) class PipKeyboardTestShellTransit(testSpec: FlickerTestParameter) : PipKeyboardTest(testSpec) { @Before @@ -43,7 +42,7 @@ class PipKeyboardTestShellTransit(testSpec: FlickerTestParameter) : PipKeyboardT Assume.assumeTrue(isShellTransitionsEnabled) } - @FlakyTest(bugId = 214452854) + @Presubmit @Test override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt deleted file mode 100644 index 21175a0767a5..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.flicker.pip - -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.FlickerTestParameter -import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.annotation.Group4 -import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.launchSplitScreen -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome -import com.android.wm.shell.flicker.helpers.BaseAppHelper.Companion.isShellTransitionsEnabled -import com.android.wm.shell.flicker.helpers.FixedAppHelper -import com.android.wm.shell.flicker.helpers.ImeAppHelper -import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP -import org.junit.Assume.assumeFalse -import org.junit.Assume.assumeTrue -import org.junit.Before -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** - * Test Pip with split-screen. - * To run this test: `atest WMShellFlickerTests:PipLegacySplitScreenTest` - */ -@RequiresDevice -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Group4 -class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { - private val imeApp = ImeAppHelper(instrumentation) - private val testApp = FixedAppHelper(instrumentation) - - @Before - open fun setup() { - // Only run legacy split tests when the system is using legacy split screen. - assumeTrue(SplitScreenHelper.isUsingLegacySplit()) - // Legacy split is having some issue with Shell transition, and will be deprecated soon. - assumeFalse(isShellTransitionsEnabled()) - } - - override val transition: FlickerBuilder.() -> Unit - get() = { - setup { - test { - removeAllTasksButHome() - device.wakeUpAndGoToHomeScreen() - pipApp.launchViaIntent(stringExtras = mapOf(EXTRA_ENTER_PIP to "true"), - wmHelper = wmHelper) - } - } - transitions { - testApp.launchViaIntent(wmHelper) - device.launchSplitScreen(wmHelper) - imeApp.launchViaIntent(wmHelper) - } - teardown { - eachRun { - imeApp.exit(wmHelper) - testApp.exit(wmHelper) - } - test { - removeAllTasksButHome() - } - } - } - - /** {@inheritDoc} */ - @FlakyTest(bugId = 206753786) - @Test - override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() - - @FlakyTest(bugId = 161435597) - @Test - fun pipWindowInsideDisplayBounds() { - testSpec.assertWmVisibleRegion(pipApp.component) { - coversAtMost(displayBounds) - } - } - - @Presubmit - @Test - fun bothAppWindowsVisible() { - testSpec.assertWmEnd { - isAppWindowVisible(testApp.component) - isAppWindowVisible(imeApp.component) - doNotOverlap(testApp.component, imeApp.component) - } - } - - @FlakyTest(bugId = 161435597) - @Test - fun pipLayerInsideDisplayBounds() { - testSpec.assertLayersVisibleRegion(pipApp.component) { - coversAtMost(displayBounds) - } - } - - @Presubmit - @Test - fun bothAppLayersVisible() { - testSpec.assertLayersEnd { - visibleRegion(testApp.component).coversAtMost(displayBounds) - visibleRegion(imeApp.component).coversAtMost(displayBounds) - } - } - - @FlakyTest(bugId = 161435597) - @Test - override fun entireScreenCovered() = super.entireScreenCovered() - - companion object { - const val TEST_REPETITIONS = 2 - - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTestParameter> { - return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( - supportedRotations = listOf(Surface.ROTATION_0), - repetitions = TEST_REPETITIONS - ) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index c1ee1a7cbb35..4618fb376f7f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -27,12 +27,10 @@ import com.android.server.wm.flicker.annotation.Group4 import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.entireScreenCovered import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.wm.shell.flicker.helpers.FixedAppHelper -import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -141,14 +139,6 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS @Presubmit @Test fun pipLayerRotates_StartingBounds() { - Assume.assumeFalse(isShellTransitionsEnabled) - pipLayerRotates_StartingBounds_internal() - } - - @FlakyTest(bugId = 228024285) - @Test - fun pipLayerRotates_StartingBounds_ShellTransit() { - Assume.assumeTrue(isShellTransitionsEnabled) pipLayerRotates_StartingBounds_internal() } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt new file mode 100644 index 000000000000..702710caded7 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2022 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.wm.shell.flicker.splitscreen + +import android.platform.test.annotations.Presubmit +import android.view.WindowManagerPolicyConstants +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.FlickerParametersRunnerFactory +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.FlickerTestParameterFactory +import com.android.server.wm.flicker.annotation.Group1 +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.wm.shell.flicker.appWindowBecomesVisible +import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd +import com.android.wm.shell.flicker.helpers.SplitScreenHelper +import com.android.wm.shell.flicker.layerBecomesVisible +import com.android.wm.shell.flicker.layerIsVisibleAtEnd +import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible +import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd +import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible +import org.junit.Assume +import org.junit.Before +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test enter split screen by dragging app icon from all apps. + * This test is only for large screen devices. + * + * To run this test: `atest WMShellFlickerTests:EnterSplitScreenByDragFromAllApps` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +@Group1 +class EnterSplitScreenByDragFromAllApps( + testSpec: FlickerTestParameter +) : SplitScreenBase(testSpec) { + + @Before + open fun before() { + Assume.assumeTrue(taplInstrumentation.isTablet) + } + + override val transition: FlickerBuilder.() -> Unit + get() = { + super.transition(this) + setup { + eachRun { + taplInstrumentation.goHome() + primaryApp.launchViaIntent(wmHelper) + } + } + transitions { + taplInstrumentation.launchedAppState.taskbar + .openAllApps() + .getAppIcon(secondaryApp.appName) + .dragToSplitscreen(secondaryApp.component.packageName, + primaryApp.component.packageName) + } + } + + @Presubmit + @Test + fun dividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible() + + @Presubmit + @Test + fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp.component) + + @Presubmit + @Test + fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp.component) + + @Presubmit + @Test + fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd( + testSpec.endRotation, primaryApp.component, false /* splitLeftTop */) + + @Presubmit + @Test + fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible( + testSpec.endRotation, secondaryApp.component, true /* splitLeftTop */) + + @Presubmit + @Test + fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp.component) + + @Presubmit + @Test + fun secondaryAppWindowBecomesVisible() = + testSpec.appWindowBecomesVisible(secondaryApp.component) + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams(): List<FlickerTestParameter> { + return FlickerTestParameterFactory.getInstance().getConfigNonRotationTests( + repetitions = SplitScreenHelper.TEST_REPETITIONS, + // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. + supportedNavigationModes = + listOf(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY)) + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt new file mode 100644 index 000000000000..52c2daf96a3c --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 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.wm.shell.flicker.splitscreen + +import android.app.Instrumentation +import android.content.Context +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.tapl.LauncherInstrumentation +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.setRotation +import com.android.wm.shell.flicker.helpers.SplitScreenHelper + +abstract class SplitScreenBase(protected val testSpec: FlickerTestParameter) { + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected val taplInstrumentation = LauncherInstrumentation() + protected val context: Context = instrumentation.context + protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation) + protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation) + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + transition(this) + } + } + + protected open val transition: FlickerBuilder.() -> Unit + get() = { + setup { + test { + taplInstrumentation.setEnableRotation(true) + setRotation(testSpec.startRotation) + taplInstrumentation.setExpectedRotation(testSpec.startRotation) + } + } + teardown { + eachRun { + primaryApp.exit(wmHelper) + secondaryApp.exit(wmHelper) + } + } + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml index 909b77c87894..e9e7bb61660d 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml @@ -44,6 +44,32 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" + android:checkedButton="@id/enter_pip_on_leave_disabled"> + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Enter PiP on home press"/> + + <RadioButton + android:id="@+id/enter_pip_on_leave_disabled" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Disabled" + android:onClick="onAutoPipSelected"/> + + <RadioButton + android:id="@+id/enter_pip_on_leave_manual" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Via code behind" + android:onClick="onAutoPipSelected"/> + </RadioGroup> + + <RadioGroup + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" android:checkedButton="@id/ratio_default"> <TextView diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java index a6ba7823e22d..a39aa4dc7bde 100644 --- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java +++ b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java @@ -48,6 +48,7 @@ import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.CheckBox; +import android.widget.RadioButton; import java.util.ArrayList; import java.util.Arrays; @@ -201,6 +202,17 @@ public class PipActivity extends Activity { super.onDestroy(); } + @Override + protected void onUserLeaveHint() { + // Only used when auto PiP is disabled. This is to simulate the behavior that an app + // supports regular PiP but not auto PiP. + final boolean manuallyEnterPip = + ((RadioButton) findViewById(R.id.enter_pip_on_leave_manual)).isChecked(); + if (manuallyEnterPip) { + enterPictureInPictureMode(); + } + } + private RemoteAction buildRemoteAction(Icon icon, String label, String action) { final Intent intent = new Intent(action); final PendingIntent pendingIntent = @@ -216,6 +228,17 @@ public class PipActivity extends Activity { enterPictureInPictureMode(mPipParamsBuilder.build()); } + public void onAutoPipSelected(View v) { + switch (v.getId()) { + case R.id.enter_pip_on_leave_manual: + // disable auto enter PiP + case R.id.enter_pip_on_leave_disabled: + mPipParamsBuilder.setAutoEnterEnabled(false); + setPictureInPictureParams(mPipParamsBuilder.build()); + break; + } + } + public void onRatioSelected(View v) { switch (v.getId()) { case R.id.ratio_default: diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java deleted file mode 100644 index e73d9aaf190a..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairTests.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.apppairs; - -import static android.view.Display.DEFAULT_DISPLAY; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager; -import android.hardware.display.DisplayManager; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.TestRunningTaskInfoBuilder; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.SyncTransactionQueue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** - * Tests for {@link AppPair} - * Build/Install/Run: - * atest WMShellUnitTests:AppPairTests - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class AppPairTests extends ShellTestCase { - - private AppPairsController mController; - @Mock private SyncTransactionQueue mSyncQueue; - @Mock private ShellTaskOrganizer mTaskOrganizer; - @Mock private DisplayController mDisplayController; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); - when(mDisplayController.getDisplay(anyInt())).thenReturn( - mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); - mController = new TestAppPairsController( - mTaskOrganizer, - mSyncQueue, - mDisplayController); - spyOn(mController); - } - - @After - public void tearDown() {} - - @Test - @UiThreadTest - public void testContains() { - final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build(); - final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build(); - - final AppPair pair = mController.pairInner(task1, task2); - assertThat(pair.contains(task1.taskId)).isTrue(); - assertThat(pair.contains(task2.taskId)).isTrue(); - - pair.unpair(); - assertThat(pair.contains(task1.taskId)).isFalse(); - assertThat(pair.contains(task2.taskId)).isFalse(); - } - - @Test - @UiThreadTest - public void testVanishUnpairs() { - final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build(); - final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build(); - - final AppPair pair = mController.pairInner(task1, task2); - assertThat(pair.contains(task1.taskId)).isTrue(); - assertThat(pair.contains(task2.taskId)).isTrue(); - - pair.onTaskVanished(task1); - assertThat(pair.contains(task1.taskId)).isFalse(); - assertThat(pair.contains(task2.taskId)).isFalse(); - } - - @Test - @UiThreadTest - public void testOnTaskInfoChanged_notSupportsMultiWindow() { - final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build(); - final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build(); - - final AppPair pair = mController.pairInner(task1, task2); - assertThat(pair.contains(task1.taskId)).isTrue(); - assertThat(pair.contains(task2.taskId)).isTrue(); - - task1.supportsMultiWindow = false; - pair.onTaskInfoChanged(task1); - verify(mController).unpair(pair.getRootTaskId()); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java deleted file mode 100644 index 505c153eff9c..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsControllerTests.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.apppairs; - -import static android.view.Display.DEFAULT_DISPLAY; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.when; - -import android.app.ActivityManager; -import android.hardware.display.DisplayManager; - -import androidx.test.annotation.UiThreadTest; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.TestRunningTaskInfoBuilder; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.SyncTransactionQueue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** Tests for {@link AppPairsController} */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class AppPairsControllerTests extends ShellTestCase { - private TestAppPairsController mController; - private TestAppPairsPool mPool; - @Mock private SyncTransactionQueue mSyncQueue; - @Mock private ShellTaskOrganizer mTaskOrganizer; - @Mock private DisplayController mDisplayController; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); - when(mDisplayController.getDisplay(anyInt())).thenReturn( - mContext.getSystemService(DisplayManager.class).getDisplay(DEFAULT_DISPLAY)); - mController = new TestAppPairsController( - mTaskOrganizer, - mSyncQueue, - mDisplayController); - mPool = mController.getPool(); - } - - @After - public void tearDown() {} - - @Test - @UiThreadTest - public void testPairUnpair() { - final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build(); - final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build(); - - final AppPair pair = mController.pairInner(task1, task2); - assertThat(pair.contains(task1.taskId)).isTrue(); - assertThat(pair.contains(task2.taskId)).isTrue(); - assertThat(mPool.poolSize()).isGreaterThan(0); - - mController.unpair(task2.taskId); - assertThat(pair.contains(task1.taskId)).isFalse(); - assertThat(pair.contains(task2.taskId)).isFalse(); - assertThat(mPool.poolSize()).isGreaterThan(1); - } - - @Test - @UiThreadTest - public void testUnpair_DontReleaseToPool() { - final ActivityManager.RunningTaskInfo task1 = new TestRunningTaskInfoBuilder().build(); - final ActivityManager.RunningTaskInfo task2 = new TestRunningTaskInfoBuilder().build(); - - final AppPair pair = mController.pairInner(task1, task2); - assertThat(pair.contains(task1.taskId)).isTrue(); - assertThat(pair.contains(task2.taskId)).isTrue(); - - mController.unpair(task2.taskId, false /* releaseToPool */); - assertThat(pair.contains(task1.taskId)).isFalse(); - assertThat(pair.contains(task2.taskId)).isFalse(); - assertThat(mPool.poolSize()).isEqualTo(1); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java deleted file mode 100644 index a3f134ee97ed..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/AppPairsPoolTests.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.apppairs; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.when; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.ShellTestCase; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.SyncTransactionQueue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -/** Tests for {@link AppPairsPool} */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class AppPairsPoolTests extends ShellTestCase { - private TestAppPairsController mController; - private TestAppPairsPool mPool; - @Mock private SyncTransactionQueue mSyncQueue; - @Mock private ShellTaskOrganizer mTaskOrganizer; - @Mock private DisplayController mDisplayController; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(mDisplayController.getDisplayContext(anyInt())).thenReturn(mContext); - mController = new TestAppPairsController( - mTaskOrganizer, - mSyncQueue, - mDisplayController); - mPool = mController.getPool(); - } - - @After - public void tearDown() {} - - @Test - public void testInitialState() { - // Pool should always start off with at least 1 entry. - assertThat(mPool.poolSize()).isGreaterThan(0); - } - - @Test - public void testAcquireRelease() { - assertThat(mPool.poolSize()).isGreaterThan(0); - final AppPair appPair = mPool.acquire(); - assertThat(mPool.poolSize()).isGreaterThan(0); - mPool.release(appPair); - assertThat(mPool.poolSize()).isGreaterThan(1); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java deleted file mode 100644 index 294bc1276291..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsController.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.apppairs; - -import static org.mockito.Mockito.mock; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.DisplayInsetsController; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.SyncTransactionQueue; - -public class TestAppPairsController extends AppPairsController { - private TestAppPairsPool mPool; - - public TestAppPairsController(ShellTaskOrganizer organizer, SyncTransactionQueue syncQueue, - DisplayController displayController) { - super(organizer, syncQueue, displayController, mock(ShellExecutor.class), - mock(DisplayImeController.class), mock(DisplayInsetsController.class)); - mPool = new TestAppPairsPool(this); - setPairsPool(mPool); - } - - TestAppPairsPool getPool() { - return mPool; - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java deleted file mode 100644 index 1ee7fff44892..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/apppairs/TestAppPairsPool.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2020 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.wm.shell.apppairs; - -import android.app.ActivityManager; - -import com.android.wm.shell.TestRunningTaskInfoBuilder; - -public class TestAppPairsPool extends AppPairsPool{ - TestAppPairsPool(AppPairsController controller) { - super(controller); - } - - @Override - void incrementPool() { - final AppPair entry = new AppPair(mController); - final ActivityManager.RunningTaskInfo info = - new TestRunningTaskInfoBuilder().build(); - entry.onTaskAppeared(info, null /* leash */); - release(entry); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java new file mode 100644 index 000000000000..44ff35466ae2 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesNavBarMotionEventHandlerTest.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2022 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.wm.shell.bubbles; + +import static android.view.MotionEvent.ACTION_CANCEL; +import static android.view.MotionEvent.ACTION_DOWN; +import static android.view.MotionEvent.ACTION_MOVE; +import static android.view.MotionEvent.ACTION_UP; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.floatThat; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.os.SystemClock; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.MotionEvent; +import android.view.WindowManager; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.bubbles.BubblesNavBarMotionEventHandler.MotionEventListener; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Test {@link MotionEvent} handling in {@link BubblesNavBarMotionEventHandler}. + * Verifies that swipe events + */ +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@RunWith(AndroidTestingRunner.class) +public class BubblesNavBarMotionEventHandlerTest extends ShellTestCase { + + private BubblesNavBarMotionEventHandler mMotionEventHandler; + @Mock + private WindowManager mWindowManager; + @Mock + private Runnable mInterceptTouchRunnable; + @Mock + private MotionEventListener mMotionEventListener; + private long mMotionEventTime; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + TestableBubblePositioner positioner = new TestableBubblePositioner(getContext(), + mWindowManager); + mMotionEventHandler = new BubblesNavBarMotionEventHandler(getContext(), positioner, + mInterceptTouchRunnable, mMotionEventListener); + mMotionEventTime = SystemClock.uptimeMillis(); + } + + @Test + public void testMotionEvent_swipeUpInGestureZone_handled() { + mMotionEventHandler.onMotionEvent(newEvent(ACTION_DOWN, 0, 990)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 690)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 490)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 390)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 390)); + + verify(mMotionEventListener).onDown(0, 990); + verify(mMotionEventListener).onMove(0, -300); + verify(mMotionEventListener).onMove(0, -500); + verify(mMotionEventListener).onMove(0, -600); + // Check that velocity up is about 5000 + verify(mMotionEventListener).onUp(eq(0f), floatThat(f -> Math.round(f) == -5000)); + verifyZeroInteractions(mMotionEventListener); + verify(mInterceptTouchRunnable).run(); + } + + @Test + public void testMotionEvent_swipeUpOutsideGestureZone_ignored() { + mMotionEventHandler.onMotionEvent(newEvent(ACTION_DOWN, 0, 500)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 100)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 100)); + + verifyZeroInteractions(mMotionEventListener); + verifyZeroInteractions(mInterceptTouchRunnable); + } + + @Test + public void testMotionEvent_horizontalMoveMoreThanTouchSlop_handled() { + mMotionEventHandler.onMotionEvent(newEvent(ACTION_DOWN, 0, 990)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 100, 990)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 100, 990)); + + verify(mMotionEventListener).onDown(0, 990); + verify(mMotionEventListener).onMove(100, 0); + verify(mMotionEventListener).onUp(0, 0); + verifyZeroInteractions(mMotionEventListener); + verify(mInterceptTouchRunnable).run(); + } + + @Test + public void testMotionEvent_moveLessThanTouchSlop_ignored() { + mMotionEventHandler.onMotionEvent(newEvent(ACTION_DOWN, 0, 990)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_MOVE, 0, 989)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_UP, 0, 989)); + + verify(mMotionEventListener).onDown(0, 990); + verifyNoMoreInteractions(mMotionEventListener); + verifyZeroInteractions(mInterceptTouchRunnable); + } + + @Test + public void testMotionEvent_actionCancel_listenerNotified() { + mMotionEventHandler.onMotionEvent(newEvent(ACTION_DOWN, 0, 990)); + mMotionEventHandler.onMotionEvent(newEvent(ACTION_CANCEL, 0, 990)); + verify(mMotionEventListener).onDown(0, 990); + verify(mMotionEventListener).onCancel(); + verifyNoMoreInteractions(mMotionEventListener); + verifyZeroInteractions(mInterceptTouchRunnable); + } + + private MotionEvent newEvent(int actionDown, float x, float y) { + MotionEvent event = MotionEvent.obtain(0L, mMotionEventTime, actionDown, x, y, 0); + mMotionEventTime += 10; + return event; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java new file mode 100644 index 000000000000..21887c03833a --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/animation/ExpandedViewAnimationControllerTest.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2022 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.wm.shell.bubbles.animation; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assume.assumeTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.ViewConfiguration; +import android.view.WindowManager; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.bubbles.BubbleExpandedView; +import com.android.wm.shell.bubbles.TestableBubblePositioner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class ExpandedViewAnimationControllerTest extends ShellTestCase { + + private ExpandedViewAnimationController mController; + + @Mock + private WindowManager mWindowManager; + + @Mock + private BubbleExpandedView mMockExpandedView; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + TestableBubblePositioner positioner = new TestableBubblePositioner(getContext(), + mWindowManager); + mController = new ExpandedViewAnimationControllerImpl(getContext(), positioner); + + mController.setExpandedView(mMockExpandedView); + when(mMockExpandedView.getContentHeight()).thenReturn(1000); + } + + @Test + public void testUpdateDrag_expandedViewMovesUpAndClipped() { + // Drag by 50 pixels which corresponds to 10 pixels with overscroll + int dragDistance = 50; + int dampenedDistance = 10; + + mController.updateDrag(dragDistance); + + verify(mMockExpandedView).setTopClip(dampenedDistance); + verify(mMockExpandedView).setContentTranslationY(-dampenedDistance); + verify(mMockExpandedView).setManageButtonTranslationY(-dampenedDistance); + } + + @Test + public void testUpdateDrag_zOrderUpdates() { + mController.updateDrag(10); + mController.updateDrag(20); + + verify(mMockExpandedView, times(1)).setSurfaceZOrderedOnTop(true); + verify(mMockExpandedView, times(1)).setAnimating(true); + } + + @Test + public void testUpdateDrag_moveBackToZero_zOrderRestored() { + mController.updateDrag(50); + reset(mMockExpandedView); + mController.updateDrag(0); + mController.updateDrag(0); + + verify(mMockExpandedView, times(1)).setSurfaceZOrderedOnTop(false); + verify(mMockExpandedView, times(1)).setAnimating(false); + } + + @Test + public void testUpdateDrag_hapticFeedbackOnlyOnce() { + // Drag by 10 which is below the collapse threshold - no feedback + mController.updateDrag(10); + verify(mMockExpandedView, times(0)).performHapticFeedback(anyInt()); + // 150 takes it over the threshold - perform feedback + mController.updateDrag(150); + verify(mMockExpandedView, times(1)).performHapticFeedback(anyInt()); + // Continue dragging, no more feedback + mController.updateDrag(200); + verify(mMockExpandedView, times(1)).performHapticFeedback(anyInt()); + // Drag below threshold and over again - no more feedback + mController.updateDrag(10); + mController.updateDrag(150); + verify(mMockExpandedView, times(1)).performHapticFeedback(anyInt()); + } + + @Test + public void testShouldCollapse_doNotCollapseIfNotDragged() { + assertThat(mController.shouldCollapse()).isFalse(); + } + + @Test + public void testShouldCollapse_doNotCollapseIfVelocityDown() { + assumeTrue("Min fling velocity should be > 1 for this test", getMinFlingVelocity() > 1); + mController.setSwipeVelocity(getVelocityAboveMinFling()); + assertThat(mController.shouldCollapse()).isFalse(); + } + + @Test + public void tesShouldCollapse_doNotCollapseIfVelocityUpIsSmall() { + assumeTrue("Min fling velocity should be > 1 for this test", getMinFlingVelocity() > 1); + mController.setSwipeVelocity(-getVelocityBelowMinFling()); + assertThat(mController.shouldCollapse()).isFalse(); + } + + @Test + public void testShouldCollapse_collapseIfVelocityUpIsLarge() { + assumeTrue("Min fling velocity should be > 1 for this test", getMinFlingVelocity() > 1); + mController.setSwipeVelocity(-getVelocityAboveMinFling()); + assertThat(mController.shouldCollapse()).isTrue(); + } + + @Test + public void testShouldCollapse_collapseIfPastThreshold() { + mController.updateDrag(500); + assertThat(mController.shouldCollapse()).isTrue(); + } + + @Test + public void testReset() { + mController.updateDrag(100); + reset(mMockExpandedView); + mController.reset(); + verify(mMockExpandedView, atLeastOnce()).setAnimating(false); + verify(mMockExpandedView).setContentAlpha(1); + verify(mMockExpandedView).setAlpha(1); + verify(mMockExpandedView).setManageButtonAlpha(1); + verify(mMockExpandedView).setManageButtonAlpha(1); + verify(mMockExpandedView).setTopClip(0); + verify(mMockExpandedView).setContentTranslationY(-0f); + verify(mMockExpandedView).setManageButtonTranslationY(-0f); + verify(mMockExpandedView).setBottomClip(0); + verify(mMockExpandedView).movePointerBy(0, 0); + assertThat(mController.shouldCollapse()).isFalse(); + } + + private int getVelocityBelowMinFling() { + return getMinFlingVelocity() - 1; + } + + private int getVelocityAboveMinFling() { + return getMinFlingVelocity() + 1; + } + + private int getMinFlingVelocity() { + return ViewConfiguration.get(getContext()).getScaledMinimumFlingVelocity(); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index ecf1c5d41864..6a6db8aa3c04 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -345,8 +345,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( false); final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); - mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0, - Surface.ROTATION_90, handlerWCT); + mSpiedOneHandedController.onDisplayChange(mDisplay.getDisplayId(), Surface.ROTATION_0, + Surface.ROTATION_90, null /* newDisplayAreaInfo */, handlerWCT); verify(mMockDisplayAreaOrganizer, atLeastOnce()).onRotateDisplay(eq(mContext), eq(Surface.ROTATION_90), any(WindowContainerTransaction.class)); @@ -358,8 +358,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( false); final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); - mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0, - Surface.ROTATION_90, handlerWCT); + mSpiedOneHandedController.onDisplayChange(mDisplay.getDisplayId(), Surface.ROTATION_0, + Surface.ROTATION_90, null /* newDisplayAreaInfo */, handlerWCT); verify(mMockDisplayAreaOrganizer, never()).onRotateDisplay(eq(mContext), eq(Surface.ROTATION_90), any(WindowContainerTransaction.class)); @@ -371,8 +371,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( true); final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); - mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0, - Surface.ROTATION_90, handlerWCT); + mSpiedOneHandedController.onDisplayChange(mDisplay.getDisplayId(), Surface.ROTATION_0, + Surface.ROTATION_90, null /* newDisplayAreaInfo */, handlerWCT); verify(mMockDisplayAreaOrganizer, never()).onRotateDisplay(eq(mContext), eq(Surface.ROTATION_90), any(WindowContainerTransaction.class)); @@ -384,8 +384,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockSettingsUitl.getSettingsSwipeToNotificationEnabled(any(), anyInt())).thenReturn( false); final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); - mSpiedOneHandedController.onRotateDisplay(mDisplay.getDisplayId(), Surface.ROTATION_0, - Surface.ROTATION_90, handlerWCT); + mSpiedOneHandedController.onDisplayChange(mDisplay.getDisplayId(), Surface.ROTATION_0, + Surface.ROTATION_90, null /* newDisplayAreaInfo */, handlerWCT); verify(mMockDisplayAreaOrganizer, atLeastOnce()).onRotateDisplay(eq(mContext), eq(Surface.ROTATION_90), any(WindowContainerTransaction.class)); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index df18133adcfb..abd55dd7d606 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -76,6 +76,7 @@ public class PipControllerTest extends ShellTestCase { @Mock private PhonePipMenuController mMockPhonePipMenuController; @Mock private PipAppOpsListener mMockPipAppOpsListener; @Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm; + @Mock private PipKeepClearAlgorithm mMockPipKeepClearAlgorithm; @Mock private PipSnapAlgorithm mMockPipSnapAlgorithm; @Mock private PipMediaController mMockPipMediaController; @Mock private PipTaskOrganizer mMockPipTaskOrganizer; @@ -101,7 +102,8 @@ public class PipControllerTest extends ShellTestCase { }).when(mMockExecutor).execute(any()); mPipController = new PipController(mContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, - mMockPipBoundsState, mMockPipMediaController, + mMockPipKeepClearAlgorithm, + mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper, mMockTaskStackListener, mPipParamsChangedForwarder, @@ -134,7 +136,8 @@ public class PipControllerTest extends ShellTestCase { assertNull(PipController.create(spyContext, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, - mMockPipBoundsState, mMockPipMediaController, + mMockPipKeepClearAlgorithm, + mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController, mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper, mMockTaskStackListener, mPipParamsChangedForwarder, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java new file mode 100644 index 000000000000..e0f7e35f8d02 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipKeepClearAlgorithmTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2022 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.wm.shell.pip.phone; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.ShellTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Set; + +/** + * Unit tests against {@link PipKeepClearAlgorithm}. + */ +@RunWith(AndroidTestingRunner.class) +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class PipKeepClearAlgorithmTest extends ShellTestCase { + + private PipKeepClearAlgorithm mPipKeepClearAlgorithm; + private static final Rect DISPLAY_BOUNDS = new Rect(0, 0, 1000, 1000); + + @Before + public void setUp() throws Exception { + mPipKeepClearAlgorithm = new PipKeepClearAlgorithm(); + } + + @Test + public void adjust_withCollidingRestrictedKeepClearAreas_movesBounds() { + final Rect inBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(50, 50, 150, 150); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect), + Set.of(), DISPLAY_BOUNDS); + + assertFalse(outBounds.contains(keepClearRect)); + } + + @Test + public void adjust_withNonCollidingRestrictedKeepClearAreas_boundsDoNotChange() { + final Rect inBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(100, 100, 150, 150); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(keepClearRect), + Set.of(), DISPLAY_BOUNDS); + + assertEquals(inBounds, outBounds); + } + + @Test + public void adjust_withCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() { + // TODO(b/183746978): update this test to accommodate for the updated algorithm + final Rect inBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(50, 50, 150, 150); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(), + Set.of(keepClearRect), DISPLAY_BOUNDS); + + assertEquals(inBounds, outBounds); + } + + @Test + public void adjust_withNonCollidingUnrestrictedKeepClearAreas_boundsDoNotChange() { + final Rect inBounds = new Rect(0, 0, 100, 100); + final Rect keepClearRect = new Rect(100, 100, 150, 150); + + final Rect outBounds = mPipKeepClearAlgorithm.adjust(inBounds, Set.of(), + Set.of(keepClearRect), DISPLAY_BOUNDS); + + assertEquals(inBounds, outBounds); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java index ffaab652aa99..b52690487944 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java @@ -340,7 +340,7 @@ public class SplitTransitionTests extends ShellTestCase { TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0); info.addChange(mainChange); info.addChange(sideChange); - IBinder transition = mSplitScreenTransitions.startDismissTransition(null, + IBinder transition = mSplitScreenTransitions.startDismissTransition( new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE); boolean accepted = mStageCoordinator.startAnimation(transition, info, @@ -363,7 +363,7 @@ public class SplitTransitionTests extends ShellTestCase { TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0); info.addChange(mainChange); info.addChange(sideChange); - IBinder transition = mSplitScreenTransitions.startDismissTransition(null, + IBinder transition = mSplitScreenTransitions.startDismissTransition( new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER, STAGE_TYPE_SIDE); mMainStage.onTaskVanished(mMainChild); diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java index f17189dedcba..491a8ec2c130 100644 --- a/media/java/android/media/audio/common/AidlConversion.java +++ b/media/java/android/media/audio/common/AidlConversion.java @@ -695,6 +695,10 @@ public class AidlConversion { aidl.type = AudioDeviceType.OUT_SPEAKER; aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; break; + case AudioSystem.DEVICE_OUT_BLE_BROADCAST: + aidl.type = AudioDeviceType.OUT_BROADCAST; + aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE; + break; case AudioSystem.DEVICE_IN_BUILTIN_MIC: aidl.type = AudioDeviceType.IN_MICROPHONE; break; diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index 9a80b0267976..87e61b527477 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -50,6 +50,7 @@ android_library { "SettingsLibTwoTargetPreference", "SettingsLibSettingsTransition", "SettingsLibButtonPreference", + "SettingsLibDeviceStateRotationLock", "setupdesign", "zxing-core-1.7", ], diff --git a/packages/SettingsLib/DeviceStateRotationLock/Android.bp b/packages/SettingsLib/DeviceStateRotationLock/Android.bp new file mode 100644 index 000000000000..c642bd14ed79 --- /dev/null +++ b/packages/SettingsLib/DeviceStateRotationLock/Android.bp @@ -0,0 +1,16 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_library { + name: "SettingsLibDeviceStateRotationLock", + + srcs: ["src/**/*.java"], + + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/DeviceStateRotationLock/AndroidManifest.xml b/packages/SettingsLib/DeviceStateRotationLock/AndroidManifest.xml new file mode 100644 index 000000000000..bce6599721e3 --- /dev/null +++ b/packages/SettingsLib/DeviceStateRotationLock/AndroidManifest.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.devicestate"> + +</manifest> diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/AndroidSecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java index 8aee576c3d04..8aee576c3d04 100644 --- a/packages/SettingsLib/src/com/android/settingslib/devicestate/AndroidSecureSettings.java +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/AndroidSecureSettings.java diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java index 4ed7e19f341d..4ed7e19f341d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/devicestate/DeviceStateRotationLockSettingsManager.java +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/DeviceStateRotationLockSettingsManager.java diff --git a/packages/SettingsLib/src/com/android/settingslib/devicestate/SecureSettings.java b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java index 10528739b2b0..10528739b2b0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/devicestate/SecureSettings.java +++ b/packages/SettingsLib/DeviceStateRotationLock/src/com.android.settingslib.devicestate/SecureSettings.java diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 1992c9bb7dbf..0aa32459948d 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laai tans vinnig"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laai tans stadig"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laai tans draadloos"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Laaidok"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Laai nie"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Gekoppel, laai nie"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Gelaai"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Stel terug"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Verwyder"</string> <string name="guest_resetting" msgid="7822120170191509566">"Stel tans gassessie terug …"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Stel gastesessie terug?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Dit sal ’n nuwe gastesessie begin en alle programme en data van die huidige sessie uitvee"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Verlaat gasmodus?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Dit sal programme en data in die huidige gastesessie uitvee"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Gaan uit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Stoor gasaktiwiteit?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Jy kan aktiwiteit in die huidige sessie stoor of alle programme en data uitvee"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Vee uit"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Stoor"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Verlaat gasmodus"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Stel jou gastesessie terug"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Verlaat gasmodus"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Alle aktiwiteit sal uitgevee word wanneer jy uitgaan"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Jy kan jou aktiwiteit stoor of uitvee wanneer jy uitgaan"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Stel terug om aktiwiteit nou uit te vee, of stoor of vee aktiwiteit uit wanneer jy uitgaan"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Kies foto"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index b5982ae533dd..7f1e6f80b868 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ኃይል በፍጥነት በመሙላት ላይ"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ኃይል በዝግታ በመሙላት ላይ"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"በገመድ-አልባ ኃይል በመሙላት ላይ"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"የኃይል መሙያ መትከያ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ባትሪ እየሞላ አይደለም"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ተገናኝቷል፣ ኃይል በመሙላት ላይ አይደለም"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ባትሪ ሞልቷል"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ዳግም አስጀምር"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"አስወግድ"</string> <string name="guest_resetting" msgid="7822120170191509566">"እንግዳን ዳግም በማስጀመር ላይ…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"የእንግዳ ክፍለ-ጊዜ ዳግም ይጀመር?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ይህ አዲስ የእንግዳ ክፍለ-ጊዜ ይጀምራል እና ሁሉንም መተግበሪያዎች እና ውሂብ አሁን ካለው ክፍለ-ጊዜ ይሰርዛል"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ከእንግዳ ሁኔታ ይውጣ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ይህ አሁን ካለው የእንግዳ ክፍለ-ጊዜ መተግበሪያዎችን እና ውሂብን ይሰርዛል"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ውጣ"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"የእንግዳ እንቅስቃሴ ይቀመጥ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"እንቅስቃሴን አሁን ካለው ክፍለ-ጊዜ ማስቀመጥ ወይም ሁሉንም መተግበሪያዎች እና ውሂብ መሰረዝ ይችላሉ"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ሰርዝ"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"አስቀምጥ"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ከእንግዳ ሁኔታ ውጣ"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"የእንግዳ ክፍለ-ጊዜን ዳግም አስጀምር"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"እንግዳ ያስወጡ"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"በሚወጡበት ጊዜ ሁሉም እንቅስቃሴዎች ይሰረዛሉ"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"በሚወጡበት ጊዜ እንቅስቃሴዎን ማስቀመጥ ወይም መሰረዝ ይችላሉ"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"የክፍለ-ጊዜ እንቅስቃሴን አሁን ለመሰረዝ ዳግም ያስጀምሩ፣ ወይም በሚወጡበት ጊዜ እንቅስቃሴን ማስቀመጥ ወይም መሰረዝ ይችላሉ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ፎቶ ይምረጡ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index cef5d438a863..3bda387aca89 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"جارٍ الشحن ببطء"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"جارٍ الشحن لاسلكيًا"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"وحدة الإرساء للشحن"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"لا يتم الشحن"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"الجهاز متصل بالشاحن، ولا يتم الشحن."</string> <string name="battery_info_status_full" msgid="1339002294876531312">"مشحونة"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"إعادة الضبط"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"إزالة"</string> <string name="guest_resetting" msgid="7822120170191509566">"جارٍ إعادة ضبط جلسة الضيف…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"هل تريد إعادة ضبط جلسة الضيف؟"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"سيؤدي إجراء إعادة الضبط إلى بدء جلسة ضيف جديدة وحذف جميع التطبيقات والبيانات من الجلسة الحالية."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"هل تريد الخروج من وضع الضيف؟"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"سيؤدي الخروج من وضع الضيف إلى حذف التطبيقات والبيانات من جلسة الضيف الحالية."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"خروج"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"هل تريد حفظ النشاط في وضع الضيف؟"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"يمكنك حفظ نشاط من الجلسة الحالية أو حذف كلّ التطبيقات والبيانات."</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"حذف"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"حِفظ"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"الخروج من وضع الضيف"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"إعادة ضبط جلسة الضيف"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"الخروج من وضع الضيف"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"سيتم حذف جميع الأنشطة عند الخروج من وضع الضيف."</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"يمكنك حفظ نشاطك أو حذفه عند الخروج من وضع الضيف."</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"يمكنك إجراء إعادة ضبط لحذف نشاط الجلسة الآن، أو حِفظ النشاط أو حذفه عند الخروج من وضع الضيف."</string> <string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"اختيار صورة"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 89cf67eee145..a2d5c1415a00 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্ৰুততাৰে চাৰ্জ হৈছে"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"লাহে লাহে চাৰ্জ হৈছে"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"বেতাঁৰৰ মাধ্যমেৰে চাৰ্জ হৈ আছে"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"চাৰ্জিং ড’ক"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"চ্চাৰ্জ কৰা নাই"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"সংযোগ হৈ আছে, চাৰ্জ হৈ থকা নাই"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"চাৰ্জ হ’ল"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ৰিছেট কৰক"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"আঁতৰাওক"</string> <string name="guest_resetting" msgid="7822120170191509566">"অতিথিৰ ছেশ্বন ৰিছেট কৰি থকা হৈছে…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"অতিথিৰ ছেশ্বন ৰিছেট কৰিবনে?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"এইটোৱে এটা অতিথিৰ ছেশ্বন আৰম্ভ কৰিব আৰু বৰ্তমানৰ ছেশ্বনটোৰ পৰা আটাইবোৰ এপ্ আৰু ডেটা মচিব"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"অতিথি ম’ডৰ পৰা বাহিৰ হ’বনে?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"এইটোৱে বৰ্তমানৰ অতিথিৰ ছেশ্বনটোৰ পৰা এপ্ আৰু ডেটা মচিব"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"বাহিৰ হওক"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"অতিথিৰ কাৰ্যকলাপ ছেভ কৰিবনে?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"আপুনি বৰ্তমানৰ ছেশ্বনটোৰ পৰা কাৰ্যকলাপ ছেভ কৰিব পাৰে অথবা আটাইবোৰ এপ্ আৰু ডেটা মচিব পাৰে"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"মচক"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ছেভ কৰক"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"অতিথি ম’ডৰ পৰা বাহিৰ হওক"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"অতিথিৰ ছেশ্বন ৰিছেট কৰক"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"অতিথিৰ ছেশ্বনৰ পৰা বাহিৰ হওক"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"বাহিৰ হওঁতে আটাইবোৰ কাৰ্যকলাপ মচা হ’ব"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"আপুনি বাহিৰ হওঁতে নিজৰ কাৰ্যকলাপ ছেভ কৰিব অথবা মচিব পাৰে"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"এতিয়াই ছেশ্বনৰ কাৰ্যকলাপ ৰিছেট কৰক অথবা মচক অথবা আপুনি বাহিৰ হওঁতে কাৰ্যকলাপ ছেভ কৰিব অথবা মচিব পাৰে"</string> <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ফট’ বাছনি কৰক"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index ca894e68e8f3..33c3e6a7fce4 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Sürətlə doldurulur"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Asta doldurulur"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Simsiz şarj edilir"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Şarj Doku"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Doldurulmur"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Qoşulub, şarj edilmir"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Şarj edilib"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Sıfırlayın"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Silin"</string> <string name="guest_resetting" msgid="7822120170191509566">"Qonaq məlumatı sıfırlanır…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Qonaq sessiyası sıfırlansın?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bu, yeni qonaq sessiyası başladacaq və cari sessiyadan bütün tətbiqləri və datanı siləcək"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Qonaq rejimindən çıxılsın?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bununla cari qonaq sessiyasındakı bütün tətbiqlər və data silinəcək"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Çıxın"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Qonaq fəaliyyəti saxlansın?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Cari sessiyadakı fəaliyyəti saxlaya və ya bütün tətbiq və datanı silə bilərsiniz"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Silin"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Yadda saxlayın"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Qonaq rejimindən çıxın"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Qonaq sessiyasını sıfırlayın"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Qonaq rejimindən çıxın"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Çıxış zamanı bütün fəaliyyətlər silinəcək"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Çıxışda fəaliyyətinizi saxlaya və ya silə bilərsiniz"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Sessiya fəaliyyətini indi silmək üçün sıfırlayın və ya çıxışda fəaliyyəti saxlaya və ya silə bilərsiniz"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Foto seçin"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 175842e8d6dc..fb202d38f3c6 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo se puni"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Stanica za punjenje"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetuj"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string> <string name="guest_resetting" msgid="7822120170191509566">"Sesija gosta se resetuje…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Želite da resetujete sesiju gosta?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Time ćete pokrenuti novu sesiju gosta i izbrisati sve aplikacije i podatke iz aktuelne sesije"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Izaći ćete iz režima gosta?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Time ćete izbrisati sve aplikacije i podatke iz aktuelne sesije gosta"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izađi"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Sačuvaćete aktivnosti gosta?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Sačuvajte aktivnosti iz aktuelne sesije ili izbrišite sve aplikacije i podatke"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Sačuvaj"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Izađi iz režima gosta"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Resetuj sesiju gosta"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Izađi iz režima gosta"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Sve aktivnosti će biti izbrisane pri izlazu"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Možete da sačuvate ili izbrišete aktivnosti pri izlazu"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetujete za brisanje aktivnosti sesije, ili sačuvajte ili izbrišite aktivnosti pri izlazu"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Izaberite sliku"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 9bb441ab16fa..12b8f1bfdb09 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хуткая зарадка"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Павольная зарадка"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бесправадная зарадка"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Зарадная док-станцыя"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не зараджаецца"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Падключана, не зараджаецца"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Зараджаны"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Скінуць"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Выдаліць"</string> <string name="guest_resetting" msgid="7822120170191509566">"Ідзе скід гасцявога сеанса…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Скінуць гасцявы сеанс?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Будзе запушчаны новы гасцявы сеанс. Усе праграмы і даныя бягучага сеанса будуць выдалены"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Выйсці з гасцявога рэжыму?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Будуць выдалены праграмы і даныя бягучага гасцявога сеанса"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Выйсці"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Захаваць дзеянні госця?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Можна захаваць даныя пра дзеянні ў бягучым сеансе ці выдаліць праграмы і даныя"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Выдаліць"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Захаваць"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Выйсці з гасцявога рэжыму"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Скінуць гасцявы сеанс"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Выйсці з гасцявога рэжыму"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Падчас выхаду будуць выдалены ўсе звесткі пра дзеянні"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Падчас выхаду можна захаваць ці выдаліць звесткі пра дзеянні"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Скіньце звесткі пра дзеянні падчас сеанса зараз. Вы таксама можаце захаваць ці выдаліць іх у час выхаду."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Выбраць фота"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 96ff08b623be..b8fe104b4eb5 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Зарежда се бавно"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Зарежда се безжично"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Докинг станция за зарежд."</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не се зарежда"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Свързано, не се зарежда"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Заредена"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Нулиране"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Премахване"</string> <string name="guest_resetting" msgid="7822120170191509566">"Сесията като гост се нулира…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Да се нулира ли сесията като гост?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Така ще стартирате нова сесия като гост и ще изтриете всички приложения и данни от текущата сесия"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Изход от режима на гост?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Така ще изтриете приложенията и данните от текущата сесия като гост"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Изход"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Запазване на активността като гост?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Можете да запазите активността от сесията или да изтриете всички прил. и данни"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Изтриване"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Запазване"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Изход от режима на гост"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Нулиране на сесията като гост"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Изход от сесията като гост"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Цялата активност ще бъде изтрита при изход"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"При изход можете да запазите активността или да я изтриете"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Нулирайте, за да изтриете активността в сесията сега. Можете също да я запазите или изтриете при изход"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Избиране на снимката"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 9cc930fbbdd1..abcd1f4333e1 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"দ্রুত চার্জ হচ্ছে"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ধীরে চার্জ হচ্ছে"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"কেবল ছাড়া চার্জ হচ্ছে"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"চার্জিং ডক"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"চার্জ হচ্ছে না"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"কানেক্ট করা থাকলেও চার্জ করা হচ্ছে না"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"চার্জ হয়েছে"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"রিসেট করুন"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"সরান"</string> <string name="guest_resetting" msgid="7822120170191509566">"গেস্ট সেশন রিসেট করা হচ্ছে..."</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"অতিথি সেশন রিসেট করতে চান?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"এটি নতুন অতিথি সেশন চালু করবে এবং বর্তমান সেশন থেকে সব অ্যাপ ও ডেটা মুছে দেবে"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"\'অতিথি মোড\' ছেড়ে বেরিয়ে আসবেন?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"এটি বর্তমান অতিথি সেশন থেকে অ্যাপ ও ডেটা মুছে দেবে"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"বেরিয়ে আসুন"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"অতিথি মোডের অ্যাক্টিভিটি সেভ করবেন?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"আপনি বর্তমান সেশন থেকে অ্যাক্টিভিটি সেভ করতে বা সব অ্যাপ ও ডেটা মুছতে পারবেন"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"মুছুন"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"সেভ করুন"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"\'অতিথি মোড\' ছেড়ে বেরিয়ে আসুন"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"অতিথি সেশন রিসেট করুন"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"\'অতিথি মোড\' ছেড়ে বেরিয়ে আসুন"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ছেড়ে বেরিয়ে যাওয়ার সময় সব অ্যাক্টিভিটি মুছে দেওয়া হবে"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ছেড়ে বেরিয়ে যাওয়ার সময় আপনি অ্যাক্টিভিটি সেভ করতে পারবেন বা মুছতে পারবেন"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"সেশন অ্যাক্টিভিটি মুছে দিতে এখন রিসেট করুন বা ছেড়ে বেরিয়ে আসার সময় আপনি অ্যাক্টিভিটি সেভ করতে বা মুছতে পারবেন"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ফটো বেছে নিন"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index f8b14525d4c0..d8e0958b23d9 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo punjenje"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Priključna stanica"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Poništi"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string> <string name="guest_resetting" msgid="7822120170191509566">"Poništavanje sesije gosta…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Poništiti sesiju gosta?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Ovim ćete pokrenuti novu sesiju gosta i izbrisati sve aplikacije i podatke iz trenutne sesije"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Napustiti način rada za gosta?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ovim ćete izbrisati aplikacije i podatke iz trenutne sesije gosta"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Napusti"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Sačuvati aktivnost gosta?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Možete sačuvati aktivnost iz ove sesije ili izbrisati sve aplikacije i podatke"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Sačuvaj"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Napusti način rada za gosta"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Poništi sesiju gosta"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Izlazak iz sesije gosta"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Sva aktivnost će se izbrisati pri napuštanju"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Možete sačuvati ili izbrisati svoju aktivnost pri izlasku"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Poništite da odmah izbrišete aktivnost iz sesije ili je možete sačuvati ili izbrisati pri izlasku"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir fotografije"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 9387d0befbd4..d8201de2ed90 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregant lentament"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregant sense fil"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de càrrega"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"No s\'està carregant"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connectat; no s\'està carregant"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restableix"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Suprimeix"</string> <string name="guest_resetting" msgid="7822120170191509566">"S\'està restablint el convidat…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vols restablir la sessió de convidat?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Aquesta acció iniciarà una nova sessió de convidat i suprimirà totes les aplicacions i dades de la sessió actual"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sortir del mode de convidat?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Aquesta acció suprimirà les aplicacions i dades de la sessió de convidat actual"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Surt"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Desar l\'activitat de convidat?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Pots desar l\'activitat de la sessió actual o suprimir totes les apps i dades"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Suprimeix"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Desa"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Surt del mode de convidat"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Restableix la sessió de convidat"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Surt del mode de convidat"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Se suprimirà tota l\'activitat en sortir"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Pots desar o suprimir l\'activitat en sortir"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Restableix la sessió per suprimir l\'activitat ara, o desa o suprimeix l\'activitat en sortir."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecciona una foto"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 52b492473455..0ad57cfdc82a 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rychlé nabíjení"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Pomalé nabíjení"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bezdrátové nabíjení"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Nabíjecí dok"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenabíjí se"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Připojeno, nenabíjí se"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Nabito"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetovat"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstranit"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetování hosta…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Resetovat relaci hosta?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tímto zahájíte novou relaci hosta a smažete všechny aplikace a data z aktuální relace"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Ukončit režim hosta?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tímto smažete aplikace a data z aktuální relace hosta"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ukončit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Uložit aktivitu hosta?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Aktivitu z aktuální relace můžete uložit, nebo všechny aplikace a data smazat"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Smazat"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Uložit"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Ukončit režim hosta"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Resetovat relaci hosta"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ukončit režim hosta"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Veškerá aktivita bude při ukončení smazána"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Aktivitu můžete při ukončení uložit nebo smazat"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Aktivitu relace můžete ihned smazat resetováním, případně ji můžete uložit nebo smazat při ukončení"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrat fotku"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 34295fda2677..6c6dd97ce79f 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Oplader hurtigt"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Oplader langsomt"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Trådløs opladning"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Dockingstation"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Oplader ikke"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tilsluttet, oplader ikke"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Opladet"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nulstil"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjern"</string> <string name="guest_resetting" msgid="7822120170191509566">"Nulstiller gæst…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vil du nulstille gæstesessionen?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Denne handling starter en ny gæstesession og sletter alle apps og data fra den aktuelle session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vil du afslutte gæstetilstanden?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Denne handling sletter apps og data fra den aktuelle gæstesession."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Luk"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vil du gemme gæsteaktiviteten?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Du kan gemme aktivitet fra den aktuelle session eller slette alle apps og data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Slet"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Gem"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Afslut gæstetilstand"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Nulstil gæstesession"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Afslut gæstesession"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Al aktivitet slettes ved afslutning"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Du kan gemme eller slette din aktivitet ved afslutning"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Nulstil for at slette sessionsaktiviteten nu, eller gem eller slet aktivitet ved afslutning"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Vælg billede"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 63d3c1eb3e09..85e42f583b0e 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Schnelles Aufladen"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Langsames Aufladen"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kabelloses Laden"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Wird am Dock geladen"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Wird nicht geladen"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Verbunden, wird nicht geladen"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Aufgeladen"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Zurücksetzen"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Entfernen"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gast wird zurückgesetzt…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Gastsitzung zurücksetzen?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Hierdurch wird eine neue Gastsitzung gestartet und alle Apps und Daten der aktuellen Sitzung werden gelöscht"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Gastmodus beenden?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Hierdurch werden Apps und Daten der aktuellen Gastsitzung gelöscht"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Beenden"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Gastaktivität speichern?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Speichere Aktivitäten der aktuellen Sitzung oder lösche alle Apps und Daten"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Löschen"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Speichern"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Gastmodus beenden"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Gastsitzung zurücksetzen"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Gastmodus beenden"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Beim Beenden werden alle Aktivitäten gelöscht"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Speichere oder lösche deine Aktivitäten beim Beenden"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Zurücksetzen, um jetzt die Sitzungsaktivitäten zu löschen, oder Aktivitäten beim Beenden speichern oder löschen"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Foto auswählen"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 1a7bb615b606..5241417fb9f1 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Αργή φόρτιση"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ασύρματη φόρτιση"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Βάση φόρτισης"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Δεν φορτίζει"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Συνδεδεμένη, δεν φορτίζει"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Φορτισμένη"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Επαναφορά"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Κατάργηση"</string> <string name="guest_resetting" msgid="7822120170191509566">"Επαναφορά επισκέπτη…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Επαναφορά περιόδου σύνδεσης επισκέπτη;"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Με αυτόν τον τρόπο θα ξεκινήσει μια νέα περίοδος σύνδεσης επισκέπτη και θα διαγραφούν όλες οι εφαρμογές και τα δεδομένα από την τρέχουσα περίοδο σύνδεσης"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Έξοδος από λειτ. επισκέπτη;"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Θα διαγραφούν εφαρμογές και δεδομένα από την τρέχουσα περίοδο σύνδεσης επισκέπτη"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Έξοδος"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Αποθήκευση δραστ. επισκέπτη;"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Αποθήκευση δραστ. τρέχουσας περιόδου σύνδεσης ή διαγραφή εφαρμογών και δεδομένων"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Διαγραφή"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Αποθήκευση"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Έξοδος από λειτ. επισκέπτη"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Επαναφορά περ. σύνδεσης επισκέπτη"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Έξοδος επισκέπτη"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Όλη η δραστηριότητα θα διαγραφεί κατά την έξοδο"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Αποθηκεύστε ή διαγράψτε τη δραστηριότητά σας κατά την έξοδο"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Επαναφορά για διαγραφή της δραστηριότητας της περιόδου σύνδεσης ή αποθήκευση ή διαγραφή κατά την έξοδο."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Επιλογή φωτογραφίας"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 506617b550bc..a7bbdb5110e8 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging dock"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index d8537fae95f3..0aa60e29c541 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging dock"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 506617b550bc..a7bbdb5110e8 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging dock"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 506617b550bc..a7bbdb5110e8 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging dock"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml index bb4de3da9300..f0aa3d6575b2 100644 --- a/packages/SettingsLib/res/values-en-rXC/strings.xml +++ b/packages/SettingsLib/res/values-en-rXC/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charging rapidly"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charging slowly"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Charging wirelessly"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging Dock"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Not charging"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connected, not charging"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Charged"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remove"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetting guest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset guest session?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"This will start a new guest session and delete all apps and data from the current session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Exit guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"This will delete apps and data from the current guest session"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Exit"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Save guest activity?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"You can save activity from the current session or delete all apps and data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Exit guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset guest session"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Exit guest"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All activity will be deleted on exit"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"You can save or delete your activity on exit"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset to delete session activity now, or you can save or delete activity on exit"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index db0a617a08bb..4672e3ec9fee 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rápidamente"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carga lenta"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carga inalámbrica"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de carga"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando."</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado; no se está cargando"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Restableciendo invitado…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"¿Quieres restablecer la sesión de invitado?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Esta acción comenzará una nueva sesión de invitado y borrará todas las apps y los datos de la sesión actual"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"¿Salir del modo de invitado?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Esta acción borrará todas las apps y los datos de la sesión de invitado actual"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Salir"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"¿Guardar actividad de invitado?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Puedes guardar la actividad de la sesión actual o borrar las apps y los datos"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Borrar"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Guardar"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Salir del modo de invitado"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Restablecer la sesión de invitado"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Salir del modo de invitado"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Cuando salgas, se borrará toda la actividad"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puedes guardar o borrar la actividad cuando salgas"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Restablece la sesión para eliminar la actividad ahora; o guarda o borra la actividad cuando salgas"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index d69b446c9ff9..24cb2eb89e18 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carga rápida"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carga lenta"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carga inalámbrica"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de carga"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"No se está cargando"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado pero sin cargar"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Restableciendo invitado…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"¿Restablecer sesión de invitado?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Se iniciará una nueva sesión de invitado y se borrarán todas las aplicaciones y datos de esta sesión"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"¿Salir del modo Invitado?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Se eliminarán todas las aplicaciones y datos de la sesión de invitado actual"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Salir"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"¿Guardar actividad de invitado?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Puedes guardar la actividad de esta sesión o eliminar todas las aplicaciones y datos"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Eliminar"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Guardar"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Salir del modo Invitado"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Restablecer sesión de invitado"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Salir del modo Invitado"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toda la actividad se eliminará cuando salgas"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puedes guardar o eliminar tu actividad al salir"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Restablece la sesión para eliminar la actividad ahora, o guarda o borra la actividad al salir"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 30f0350c9dd7..bc847e2b24fb 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Kiirlaadimine"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Aeglaselt laadimine"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Juhtmevaba laadimine"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Laadimisdokk"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei lae"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ühendatud, ei laeta"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Laetud"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Lähtesta"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eemalda"</string> <string name="guest_resetting" msgid="7822120170191509566">"Külastajaseansi lähtestamine …"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Kas lähtestada külastajaseanss?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"See alustab uut külastajaseanssi ning kustutab kõik praeguse seansi rakendused ja andmed."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Kas väljuda külalisrežiimist?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"See kustutab praeguse külastajaseansi rakendused ja andmed"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Välju"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Kas salvestada külalise tegevus?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Võite selle seansi tegevused salvestada või kustutada kõik rakendused ja andmed."</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Kustuta"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvesta"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Välju külalisrežiimist"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Lähtesta külastajaseanss"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Välju külastajaseansist"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Kõik tegevused kustutatakse väljumisel"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Võite tegevused salvestada või kustutada väljumisel."</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Seansi tegevuste kohe kustutamiseks lähtestage; või salvestage või kustutage need väljumisel."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Valige foto"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 3a38abdc307f..6795662cac3d 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mantso kargatzen"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hari gabe kargatzen"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Oinarrian kargatzen"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ez da kargatzen ari"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Konektatuta dago, baina ez da kargatzen ari"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Kargatuta"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Berrezarri"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Kendu"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gonbidatuentzako saioa berrezartzen…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Gonbidatuentzako saioa berrezarri nahi duzu?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Gonbidatuentzako saio berri bat abiaraziko da, eta saio honetako aplikazio eta datu guztiak ezabatuko"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Gonbidatu modutik irten nahi duzu?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Gonbidatuentzako saio honetako aplikazioak eta datuak ezabatuko dira"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Irten"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Gonbidatuaren jarduerak gorde nahi dituzu?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Saio honetako jarduerak gorde ditzakezu, edo aplikazio eta datu guztiak ezabatu"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Ezabatu"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Gorde"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Irten gonbidatu modutik"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Berrezarri gonbidatuentzako saioa"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Irten gonbidatu modutik"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Irtetean, jarduera guztiak ezabatuko dira"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Irtetean, jarduerak gorde edo ezabatu egin ditzakezu"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Berrezarri saioa jarduerak ezabatzeko; bestela, aukeratu jarduerak irtetean gordetzea edo ezabatzea"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Hautatu argazki bat"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 04549b2d46f4..52ff02172338 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"درحال شارژ شدن سریع"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"درحال شارژ شدن آهسته"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"درحال شارژ بیسیم"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"پایه شارژ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"شارژ نمیشود"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"متصل، شارژ نمیشود"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"شارژ کامل شد"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"بازنشانی"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"برداشتن"</string> <string name="guest_resetting" msgid="7822120170191509566">"درحال بازنشانی مهمان…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"جلسه مهمان بازنشانی شود؟"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"با این کار، جلسه مهمان جدیدی شروع خواهد شد و همه برنامهها و دادهها از جلسه کنونی حذف خواهند شد"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"از حالت مهمان خارج میشوید؟"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"با این کار، برنامهها و دادهها از جلسه مهمان کنونی حذف خواهند شد."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"خروج"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"فعالیت مهمان ذخیره شود؟"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"میتوانید فعالیت جلسه کنونی را ذخیره کنید یا همه برنامه و دادهها را حذف کنید"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"حذف"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ذخیره"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"خروج از حالت مهمان"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"بازنشاندن جلسه مهمان"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"خروج مهمان"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"همه فعالیتها هنگام خروج حذف خواهد شد"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"میتوانید فعالیتتان را هنگام خروج ذخیره یا حذف کنید"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"برای حذف فعالیت جلسه در این لحظه، بازنشانی کنید یا میتوانید فعالیت را هنگام خروج ذخیره یا حذف کنید"</string> <string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"انتخاب عکس"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index b643784cbbe2..d383919a9d73 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Nopea lataus"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Hidas lataus"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Langaton lataus"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Latausteline"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ei laturissa"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Yhdistetty, ei ladata"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Ladattu"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nollaa"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Poista"</string> <string name="guest_resetting" msgid="7822120170191509566">"Nollataan vierasta…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Nollataanko vierailija-käyttökerta?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tämä aloittaa uuden vierailija-käyttökerran ja kaikki nykyisen istunnon sovellukset ja data poistetaan"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Poistutaanko vierastilasta?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tämä poistaa nykyisen vierailija-käyttökerran sovellukset ja datan"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sulje"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Tallennetaanko vierastoiminta?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Voit tallentaa tämän istunnon toimintaa tai poistaa kaikki sovellukset ja datan"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Poista"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Tallenna"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Poistu vierastilasta"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Nollaa vierailija-käyttökerta"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Kirjaa vieras ulos"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Kaikki toiminta poistetaan uloskirjaamisen aikana"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Voit tallentaa tai poistaa toiminnan poistuessasi"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Nollaa poistaaksesi istunnon toiminnan nyt, tai voit tallentaa tai poistaa toimintaa poistuessasi"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Valitse kuva"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 5325fbc2df41..24ce0012917c 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Recharge lente"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En recharge sans fil"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Station de recharge"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"N\'est pas en charge"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connecté, pas en charge"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Réinitialiser"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Retirer"</string> <string name="guest_resetting" msgid="7822120170191509566">"Réinitialisation de la session Invité en cours…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Réinitialiser la session d\'invité?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Une nouvelle session d\'invité sera lancée, et toutes les applications et données de la session en cours seront supprimées"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Quitter le mode Invité?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Les applications et les données de la session d\'invité en cours seront supprimées"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Quitter"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Enregistrer l\'activité d\'invité?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Vous pouvez enregistrer l\'activité de session ou supprimer les applis et données"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Supprimer"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Enregistrer"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Quitter le mode Invité"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Réinitialiser la session d\'invité"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Quitter la session d\'invité"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toute l\'activité sera supprimée à la fin de la session"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Vous pouvez enregistrer ou supprimer votre activité à la fin"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Réinitialisez pour supprimer l\'activité de la session maintenant, ou vous pouvez enregistrer ou supprimer l\'activité à la fin de la session"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionnez une photo"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index b667fc0519b2..637cc8a2cc00 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Charge rapide"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Charge lente"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"En charge sans fil"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Station de charge"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Pas en charge"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Connectée, pas en charge"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Chargée"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Réinitialiser"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Supprimer"</string> <string name="guest_resetting" msgid="7822120170191509566">"Réinitialisation de la session Invité…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Réinitialiser la session Invité ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Cette action lancera une nouvelle session Invité et supprimera toutes les applis et données de la session actuelle"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Quitter le mode Invité ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Cette action supprimera les applis et données de la session Invité actuelle."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Quitter"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Enregistrer l\'activité ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Enregistrez l\'activité de la session actuelle ou supprimez les applis et données"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Supprimer"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Enregistrer"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Quitter le mode Invité"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Réinitialiser la session Invité"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Quitter le mode Invité"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toute l\'activité sera supprimée à la fin de la session"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Vous pouvez enregistrer ou supprimer l\'activité en quittant"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Réinitialisez la session pour supprimer immédiatement l\'activité. Vous pourrez aussi l\'enregistrer ou la supprimer en quittant la session."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionner une photo"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index da66e922543e..211f8a58f9b3 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Cargando lentamente"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Cargando sen fíos"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de carga"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Non se está cargando"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado, sen cargar"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Cargada"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Restablecer"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Quitar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Restablecendo sesión de convidado…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Queres restablecer a sesión de convidado?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Iniciarase unha nova sesión de convidado e eliminaranse todas as aplicacións e datos desta sesión"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Saír do modo de convidado?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Eliminaranse as aplicacións e os datos da sesión de convidado actual"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Saír"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Gardar actividade do convidado?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Podes gardar a actividade da sesión ou eliminar todas as aplicacións e datos"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Eliminar"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Gardar"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Saír do modo de convidado"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Restablecer sesión de convidado"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Saír do modo de convidado"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Eliminarase toda a actividade ao saír"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Podes gardar ou eliminar a túa actividade ao saír"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Restablece a sesión para eliminar a actividade agora, ou ben gárdaa ou elimínaa ao saír"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index de2fc038cfd3..0344f4f0e55f 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ઝડપથી ચાર્જ થાય છે"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ધીમેથી ચાર્જ થાય છે"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"વાયરલેસથી ચાર્જિંગ"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ડૉકથી ચાર્જ થઈ રહ્યું છે"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ચાર્જ થઈ રહ્યું નથી"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"કનેક્ટ કરેલું છે, પણ ચાર્જ થઈ રહ્યું નથી"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ચાર્જ થયું"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"રીસેટ કરો"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"કાઢી નાખો"</string> <string name="guest_resetting" msgid="7822120170191509566">"અતિથિને રીસેટ કરી રહ્યાં છીએ…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"શું અતિથિ સત્ર રીસેટ કરીએ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"આમ કરવાથી કોઈ નવું અતિથિ સત્ર ચાલુ થશે તેમજ હાલના સત્રમાંની તમામ ઍપ અને ડેટા ડિલીટ થશે"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"શું અતિથિ મોડમાંથી બહાર નીકળીએ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"આમ કરવાથી હાલના અતિથિ સત્રની તમામ ઍપ અને ડેટા ડિલીટ કરવામાં આવશે"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"બહાર નીકળો"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"શું અતિથિ પ્રવૃત્તિ સાચવીએ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"તમે હાલના સત્રની પ્રવૃત્તિ સાચવી શકો છો અથવા તમામ ઍપ અને ડેટા ડિલીટ કરી શકો છો"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ડિલીટ કરો"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"સાચવો"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"અતિથિ મોડમાંથી બહાર નીકળો"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"અતિથિ સત્ર રીસેટ કરો"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"અતિથિ મોડમાંથી બહાર નીકળો"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"બહાર નીકળતી વખતે તમામ પ્રવૃત્તિ ડિલીટ કરવામાં આવશે"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"બહાર નીકળતી વખતે તમે પ્રવૃત્તિ સાચવી કે ડિલીટ કરી શકશો"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"સત્રની પ્રવૃત્તિ હમણાં ડિલીટ કરવા માટે રીસેટ કરો અથવા બહાર નીકળતી વખતે તમે પ્રવૃત્તિ સાચવી કે ડિલીટ કરી શકશો"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ફોટો પસંદ કરો"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 9eccbf99e6b2..85220983f63f 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"तेज़ चार्ज हो रही है"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"धीरे चार्ज हो रही है"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस चार्जिंग"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"चार्जिंग डॉक"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज नहीं हो रही है"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"कनेक्ट किया गया, चार्ज नहीं हो रहा है"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"बैटरी चार्ज हो गई"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रीसेट करें"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"हटाएं"</string> <string name="guest_resetting" msgid="7822120170191509566">"मेहमान के तौर पर ब्राउज़ करने का सेशन रीसेट किया जा रहा है…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"क्या मेहमान मोड के मौजूदा सेशन को रीसेट करना है?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ऐसा करने पर, मेहमान के तौर पर ब्राउज़ करने का एक नया सेशन शुरू हो जाएगा. साथ ही, पिछले सेशन में मौजूद डेटा और इस्तेमाल किए जा रहे ऐप्लिकेशन को मिटा दिया जाएगा"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"मेहमान मोड से बाहर निकलना है?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"इससे, मेहमान मोड के मौजूदा सेशन का डेटा और इसमें इस्तेमाल हो रहे ऐप मिट जाएंगे"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"बाहर निकलें"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"मेहमान मोड की गतिविधि को सेव करना है?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"मौजूदा सेशन की गतिविधि को सेव किया जा सकता है या सभी ऐप और डेटा को मिटाया जा सकता है"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"मिटाएं"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"सेव करें"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"मेहमान मोड से बाहर निकलें"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"मेहमान मोड के सेशन को रीसेट करें?"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"मेहमान मोड से बाहर निकलें"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"बाहर निकलने पर, सारी गतिविधि मिट जाएगी"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"बाहर निकलने पर, गतिविधि को मिटाया या सेव किया जा सकता है"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"सेशन की गतिविधि को अभी मिटाने के लिए, रीसेट करें. इसके अलावा, बाहर निकलने पर, गतिविधि को मिटाया या सेव किया जा सकता है"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"फ़ोटो चुनें"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index ffd276b5c351..73b7e1a9b32f 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo punjenje"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sporo punjenje"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bežično punjenje"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Stanica za punjenje"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ne puni se"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, ne puni se"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Napunjeno"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Poništi"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ukloni"</string> <string name="guest_resetting" msgid="7822120170191509566">"Poništavanje gostujuće sesije…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Želite li poništiti gostujuću sesiju?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Time će se pokrenuti nova gostujuća sesija i izbrisati sve aplikacije i podaci iz trenutačne sesije."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Napustiti način rada za goste?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Time će se izbrisati aplikacije i podaci iz trenutačne gostujuće sesije."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Izlaz"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Spremiti aktivnosti gosta?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Možete spremiti aktivnosti iz ove sesije ili izbrisati sve aplikacije i podatke"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Spremi"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Izlaz iz načina rada za goste"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Poništi gostujuću sesiju"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Izlaz iz gostujuće sesije"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Sve će se aktivnosti izbrisati na izlasku"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Svoje aktivnosti možete spremiti ili izbrisati na izlasku."</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Poništite da odmah izbrišete aktivnost sesije. Inače je možete spremiti ili izbrisati na izlasku."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir slike"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index c613d0a7197e..fb8a7372936c 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Gyorstöltés"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lassú töltés"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Vezeték nélküli töltés"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Töltődokk"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nem tölt"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Csatlakoztatva, nem töltődik"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Feltöltve"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Visszaállítás"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eltávolítás"</string> <string name="guest_resetting" msgid="7822120170191509566">"Vendég munkamenet visszaállítása…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Visszaállítja a vendégmunkamenetet?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"A művelettel új vendégmunkamenetet indít, valamint az összes alkalmazást és adatot törli az aktuális munkamenetből"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Kilép a vendég módból?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"A művelettel törlődnek az aktuális vendégmunkamenet alkalmazásai és adatai"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Kilépés"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Menti a vendégtevékenységeket?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Tevékenységeket menthet az aktuális munkamenetből, vagy minden appot és adatot törölhet"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Törlés"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Mentés"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Kilépés a vendég módból"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Vendégmunkamenet visszaállítása"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Vendég kiléptetése"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"A kilépéssel minden tevékenység törlődik"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"A kilépéskor mentheti vagy törölheti a tevékenységeket"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Visszaállítással azonnal törölheti, illetve kilépéskor mentheti vagy törölheti a tevékenységeket"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Fotó kiválasztása"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index c7a8e4d3727a..7176ae500425 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Դանդաղ լիցքավորում"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Անլար լիցքավորում"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Լիցքավորում դոկ-կայանի միջոցով"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Չի լիցքավորվում"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Միացված է, չի լիցքավորվում"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Լիցքավորված է"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Զրոյացնել"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Հեռացնել"</string> <string name="guest_resetting" msgid="7822120170191509566">"Հյուրի աշխատաշրջանը վերակայվում է…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Զրոյացնե՞լ հյուրի աշխատաշրջանը"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Կսկսվի հյուրի նոր աշխատաշրջան, իսկ նախորդ աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Դուրս գա՞լ հյուրի ռեժիմից"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Հյուրի ընթացիկ աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Դուրս գալ"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Պահե՞լ հյուրի ռեժիմի պատմությունը"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Պահեք ընթացիկ աշխատաշրջանի պատմությունը կամ ջնջեք հավելվածներն ու տվյալները"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Ջնջել"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Պահել"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Դուրս գալ հյուրի ռեժիմից"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Զրոյացնել հյուրի աշխատաշրջանը"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Դուրս գալ հյուրի ռեժիմից"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Եթե դուրս գաք, ամբողջ պատմությունը կջնջվի"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Դուրս գալիս կարող եք պահել կամ ջնջել ձեր պատմությունը"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Վերակայեք աշխատաշրջանի պատմությունը հիմա կամ պահեք/ջնջեք այն դուրս գալիս"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Ընտրեք լուսանկար"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 3104fe17908b..6e1bff8c16da 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengisi daya cepat"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mengisi daya lambat"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Mengisi daya nirkabel"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Mengisi Daya di Dok"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Tidak mengisi daya"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Terhubung, tidak mengisi daya"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Terisi"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Hapus"</string> <string name="guest_resetting" msgid="7822120170191509566">"Mereset tamu …"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Reset sesi tamu?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tindakan ini akan memulai sesi tamu baru dan menghapus semua aplikasi serta data dari sesi saat ini"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Keluar dari mode tamu?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tindakan ini akan menghapus aplikasi dan data dari sesi tamu saat ini"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Keluar"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Simpan aktivitas tamu?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Anda bisa menyimpan aktivitas sesi saat ini atau menghapus semua aplikasi & data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Hapus"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Simpan"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Keluar dari mode tamu"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reset sesi tamu"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Keluar dari mode tamu"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Semua aktivitas akan dihapus saat Anda keluar"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Anda dapat menyimpan atau menghapus aktivitas saat keluar"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reset untuk hapus aktivitas sesi sekarang, atau simpan atau hapus aktivitas saat keluar"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index c48142490512..9c8933df6abe 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Hæg hleðsla"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Hleður þráðlaust"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Hleður í dokku"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ekki í hleðslu"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tengt, ekki í hleðslu"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Fullhlaðin"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Endurstilla"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjarlægja"</string> <string name="guest_resetting" msgid="7822120170191509566">"Endurstillir gest…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Endurstilla gestalotu?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Þetta opnar nýja gestalotu og eyðir öllum forritum og gögnum úr núverandi lotu"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Loka gestastillingu?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Þetta eyðir forritum og gögnum úr núverandi gestalotu"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Hætta"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vista aðgerðir úr gestalotu?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Þú getur vistað aðgerðir úr núverandi lotu eða eytt öllum forritum og gögnum"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Eyða"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Vista"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Loka gestastillingu"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Endurstilla gestalotu"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Loka gestastillingu"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Öllum aðgerðum verður eytt þegar lotu er lokað"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Þú getur vistað eða eytt aðgerðum þegar þú lokar"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Endurstilltu til að eyða aðgerðum lotu núna, eða vistaðu eða eyddu aðgerðum þegar þú lokar"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Velja mynd"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 87fc4b7392e7..52ef9684ee77 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ricarica veloce"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Ricarica lenta"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"In carica, wireless"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"In carica nel dock"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Non in carica"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Dispositivo connesso, non in carica"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carica"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Reimposta"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Rimuovi"</string> <string name="guest_resetting" msgid="7822120170191509566">"Reimpostazione sessione Ospite in corso…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vuoi reimpostare la sessione Ospite?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Verrà avviata una nuova sessione Ospite e verranno eliminati tutti i dati e le app della sessione corrente"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vuoi uscire da modalità Ospite?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Verranno eliminati i dati e le app della sessione Ospite corrente"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Esci"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vuoi salvare l\'attività Ospite?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Puoi salvare l\'attività della sessione corrente o eliminare tutti i dati e le app"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Elimina"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salva"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Esci dalla modalità Ospite"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Reimposta sessione Ospite"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Esci dalla modalità Ospite"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Quando esci verrà eliminata tutta l\'attività"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Quando esci puoi salvare o eliminare la tua attività"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reimposta la sessione per eliminare subito l\'attività, oppure salvala o eliminala quando esci"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Seleziona la foto"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 5c3c304ffacd..d95590a66fbf 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"הסוללה נטענת מהר"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"הסוללה נטענת לאט"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"בטעינה אלחוטית"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"אביזר עגינה לטעינה"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"לא בטעינה"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"מחובר, לא בטעינה"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"הסוללה טעונה"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"איפוס"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"הסרה"</string> <string name="guest_resetting" msgid="7822120170191509566">"מתבצע איפוס של הגלישה כאורח…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"לאפס את הגלישה כאורח?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"הפעולה הזו תתחיל גלישה חדשה כאורח ותמחק את כל האפליקציות והנתונים מהסשן הנוכחי"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"לצאת ממצב אורח?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"הפעולה הזו תמחק את האפליקציות והנתונים מהגלישה הנוכחית כאורח"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"יציאה"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"לשמור את פעילות האורח?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"אפשר לשמור את הפעילות מהסשן הנוכחי או למחוק את כל האפליקציות והנתונים"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"מחיקה"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"שמירה"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"יציאה ממצב אורח"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"איפוס הגלישה כאורח"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"יציאה ממצב אורח"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"כל הפעילות תימחק ביציאה"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"אפשר לשמור או למחוק את הפעילות שלך ביציאה"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ניתן לאפס כדי למחוק את הפעילות מהסשן כעת, או לשמור או למחוק את הפעילות ביציאה"</string> <string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"בחירת תמונה"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index cf112379e68a..5fa42a26d553 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"急速充電中"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"低速充電中"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ワイヤレス充電中"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"充電ホルダー"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"充電していません"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"接続済み、充電していません"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"充電が完了しました"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"リセット"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"削除"</string> <string name="guest_resetting" msgid="7822120170191509566">"ゲストをリセットしています…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ゲスト セッションをリセットしますか?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"新しいゲスト セッションが開始し、現在のセッションのすべてのアプリとデータが削除されます"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ゲストモードを終了しますか?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"現在のゲスト セッションからすべてのアプリとデータが削除されます"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"終了"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ゲストアクティビティの保存"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"現在のセッションのアクティビティの保存や、すべてのアプリとデータの削除を行えます"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"削除"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"保存"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ゲストモードを終了"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"ゲスト セッションをリセット"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ゲストを終了"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"終了時にすべてのアクティビティが削除されます"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"終了時にアクティビティを保存、削除できます"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"アクティビティは、リセットして今すぐ削除するか、終了時に保存または削除できます"</string> <string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"写真を選択"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index 540f1fc870d6..8ea09619e3b9 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"სწრაფად იტენება"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ნელა იტენება"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"უსადენოდ დატენა"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"დამტენი სამაგრი"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"არ იტენება"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"დაკავშირებულია, არ იტენება"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"დატენილია"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"გადაყენება"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ამოშლა"</string> <string name="guest_resetting" msgid="7822120170191509566">"მიმდინარეობს სტუმრის გადაყენება…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"გსურთ სტუმრის სესიის გადაყენება?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ამ ქმედებით დაიწყება სტუმრის ახალი სესია და წაიშლება ყველა აპი და მონაცემი მიმდინარე სესიიდან"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"გსურთ სტუმრის რეჟიმიდან გასვლა?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ეს ქმედება წაშლის აპებსა და მონაცემებს სტუმრის რეჟიმის მიმდინარე სესიიდან"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"გასვლა"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"გსურთ სტუმრის აქტივობის შენახვა?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"შეგიძლიათ შეინახოთ აქტივობა მიმდინარე სესიიდან ან წაშალოთ ყველა აპი და მონაცემი"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"წაშლა"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"შენახვა"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"სტუმრის რეჟიმიდან გასვლა"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"სტუმრის სესიის გადაყენება"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"სტუმრის გასვლა"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"გასვლისას ყველა აქტივობა წაიშლება"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"გასვლისას შეგიძლიათ შეინახოთ ან წაშალოთ თქვენი აქტივობა"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"გადააყენეთ სესიის აქტივობის ახლა წასაშლელად. შენახვა/წაშლა გასვლისასაც შეიძლება."</string> <string name="user_image_take_photo" msgid="467512954561638530">"ფოტოს გადაღება"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"აირჩიეთ სურათი"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ფოტოს არჩევა"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 22774799a853..91aea7bd907f 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядталуда"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Баяу зарядталуда"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Сымсыз зарядталуда"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Қондыру станциясы зарядталуда"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Зарядталу орындалып жатқан жоқ"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Жалғанған, зарядталып жатқан жоқ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Зарядталды"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Бастапқы күйге қайтару"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Өшіру"</string> <string name="guest_resetting" msgid="7822120170191509566">"Қонақ сеансы бастапқы күйге қайтарылуда…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Қонақ сеансын бастапқы күйге қайтару керек пе?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Мұндайда жаңа қонақ сеансы басталады және ағымдағы сеанстағы барлық қолданба мен дерек жойылады."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Қонақ режимінен шығу керек пе?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ағымдағы қонақ сеансындағы барлық қолданба мен дерек жойылады."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Шығу"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Қонақ әрекетін сақтау керек пе?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Ағымдағы сеанстағы әрекетті сақтай не барлық қолданба мен деректі жоя аласыз."</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Жою"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Сақтау"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Қонақ режимінен шығу"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Қонақ режимін қалпына келтіру"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Қонақ режимінен шығу"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Шыққанда барлық әрекет жойылады."</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Шыққанда барлық әрекетті сақтай немесе жоя аласыз."</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Сеанс дерегін жою үшін оны бастапқы күйге қайтарыңыз. Деректі шығар кезде де сақтауға не жоюға болады."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Фотосурет таңдау"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index eb8ae476b986..fcbe6e5edb31 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"កំពុងសាកថ្មយ៉ាងឆាប់រហ័ស"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"កំពុងសាកថ្មយឺត"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"កំពុងសាកថ្មឥតខ្សែ"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ឧបករណ៍ភ្ជាប់សាកថ្ម"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"មិនកំពុងសាកថ្ម"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"បានភ្ជាប់ មិនកំពុងសាកថ្ម"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"បានសាកថ្មពេញ"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"កំណត់ឡើងវិញ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ដកចេញ"</string> <string name="guest_resetting" msgid="7822120170191509566">"កំពុងកំណត់ភ្ញៀវឡើងវិញ…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"កំណត់វគ្គភ្ញៀវឡើងវិញឬ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ការធ្វើបែបនេះនឹងចាប់ផ្ដើមវគ្គភ្ញៀវថ្មី និងលុបកម្មវិធី និងទិន្នន័យទាំងអស់ចេញពីវគ្គបច្ចុប្បន្ន"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ចាកចេញពីមុខងារភ្ញៀវឬ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ការធ្វើបែបនេះនឹងលុបកម្មវិធី និងទិន្នន័យចេញពីវគ្គភ្ញៀវបច្ចុប្បន្ន"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ចាកចេញ"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"រក្សាទុកសកម្មភាពភ្ញៀវឬ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"អ្នកអាចរក្សាទុកសកម្មភាពពីវគ្គបច្ចុប្បន្ន ឬលុបកម្មវិធីនិងទិន្នន័យទាំងអស់"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"លុប"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"រក្សាទុក"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ចាកចេញពីមុខងារភ្ញៀវ"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"កំណត់វគ្គភ្ញៀវឡើងវិញ"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ចាកចេញពីមុខងារភ្ញៀវ"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"សកម្មភាពទាំងអស់នឹងត្រូវលុបនៅពេលចាកចេញ"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"អ្នកអាចរក្សាទុក ឬលុបសកម្មភាពរបស់អ្នកនៅពេលចាកចេញ"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"កំណត់ឡើងវិញ ដើម្បីលុបសកម្មភាពក្នុងវគ្គឥឡូវនេះ ឬអ្នកអាចរក្សាទុកឬលុបសកម្មភាពនៅពេលចាកចេញ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើសរូបភាព"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ជ្រើសរើសរូបថត"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 0536960094d1..4f3b1b11410e 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ನಿಧಾನ ಗತಿಯ ಚಾರ್ಜಿಂಗ್"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ವೈರ್ಲೆಸ್ ಚಾರ್ಜಿಂಗ್"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ಚಾರ್ಜಿಂಗ್ ಡಾಕ್"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ಕನೆಕ್ಟ್ ಆಗಿದೆ, ಚಾರ್ಜ್ ಆಗುತ್ತಿಲ್ಲ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ಚಾರ್ಜ್ ಆಗಿದೆ"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ರೀಸೆಟ್ ಮಾಡಿ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ತೆಗೆದುಹಾಕಿ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ಅತಿಥಿ ಬಳಕೆದಾರರ ಸೆಟ್ಟಿಂಗ್ ಅನ್ನು ರೀಸೆಟ್ ಮಾಡಲಾಗುತ್ತಿದೆ…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ಅತಿಥಿ ಸೆಷನ್ ಅನ್ನು ರೀಸೆಟ್ ಮಾಡಬೇಕೇ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ಈ ಪ್ರಕ್ರಿಯೆಯು ಹೊಸ ಅತಿಥಿ ಸೆಶನ್ ಅನ್ನು ಪ್ರಾರಂಭಿಸುತ್ತದೆ ಮತ್ತು ಪ್ರಸ್ತುತ ಸೆಶನ್ನಿಂದ ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಹಾಗೂ ಡೇಟಾವನ್ನು ಅಳಿಸುತ್ತದೆ"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ಅತಿಥಿ ಮೋಡ್ನಿಂದ ನಿರ್ಗಮಿಸಬೇಕೆ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ಈ ಪ್ರಕ್ರಿಯೆಯು ಪ್ರಸ್ತುತ ಅತಿಥಿ ಸೆಷನ್ನಿಂದ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸುತ್ತದೆ"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ನಿರ್ಗಮಿಸಿ"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ಅತಿಥಿ ಚಟುವಟಿಕೆಯನ್ನು ಉಳಿಸಬೇಕೆ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ನೀವು ಪ್ರಸ್ತುತ ಸೆಶನ್ನ ಚಟುವಟಿಕೆಯನ್ನು ಉಳಿಸಬಹುದು ಅಥವಾ ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಬಹುದು"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ಅಳಿಸಿ"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ಉಳಿಸಿ"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ಅತಿಥಿ ಮೋಡ್ನಿಂದ ನಿರ್ಗಮಿಸಿ"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"ಅತಿಥಿ ಸೆಷನ್ ಅನ್ನು ರೀಸೆಟ್ ಮಾಡಿ"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ಅತಿಥಿ ಮೋಡ್ನಿಂದ ನಿರ್ಗಮಿಸಿ"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ನಿರ್ಗಮಿಸುವಾಗ ಎಲ್ಲಾ ಚಟುವಟಿಕೆಯನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ನಿರ್ಗಮಿಸುವಾಗ ನಿಮ್ಮ ಚಟುವಟಿಕೆಯನ್ನು ನೀವು ಉಳಿಸಬಹುದು ಅಥವಾ ಅಳಿಸಬಹುದು"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ಸೆಶನ್ ಚಟುವಟಿಕೆಯನ್ನು ಈಗ ಅಳಿಸಲು ರೀಸೆಟ್ ಮಾಡಿ ಅಥವಾ ನಿರ್ಗಮಿಸುವಾಗ ನೀವು ಚಟುವಟಿಕೆಯನ್ನು ಉಳಿಸಬಹುದು ಅಥವಾ ಅಳಿಸಬಹುದು"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ಫೋಟೋ ಆಯ್ಕೆಮಾಡಿ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 4fb2385aa655..2133724d8ad3 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"고속 충전 중"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"저속 충전 중"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"무선 충전 중"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"충전 도크"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"충전 안함"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"연결됨, 충전 중 아님"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"충전됨"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"재설정"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"삭제"</string> <string name="guest_resetting" msgid="7822120170191509566">"게스트 재설정 중…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"게스트 세션을 재설정하시겠습니까?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"새로운 게스트 세션이 시작되고 기존 세션의 모든 앱과 데이터가 삭제됩니다."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"게스트 모드를 종료하시겠습니까?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"현재 게스트 세션의 앱과 데이터가 삭제됩니다."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"종료"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"게스트 활동을 저장하시겠습니까?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"기존 세션의 활동을 저장하거나 모든 앱과 데이터를 삭제할 수 있습니다."</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"삭제"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"저장"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"게스트 모드 종료"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"게스트 세션 재설정"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"게스트 종료"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"종료하면 모든 활동이 삭제됩니다."</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"종료 시 활동을 저장하거나 삭제할 수 있습니다."</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"지금 재설정하여 활동 세션을 삭제하거나 종료 시 활동을 저장 또는 삭제할 수 있습니다."</string> <string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"사진 선택"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index b4f78170c548..b10056f1dea0 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ыкчам кубатталууда"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Жай кубатталууда"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Зымсыз кубатталууда"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Кубаттоо док бекети"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Кубат алган жок"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Туташты, кубатталган жок"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Кубатталды"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Баштапкы абалга келтирүү"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Өчүрүү"</string> <string name="guest_resetting" msgid="7822120170191509566">"Конок сеансы баштапкы абалга келтирилүүдө…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Конок сеансын баштапкы абалга келтиресизби?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Бул аракет жаңы конок сеансын баштап, учурдагы сеанстагы бардык колдонмолорду жана алардагы нерселерди жок кылат"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Конок режиминен чыгасызбы?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Бул учурдагы конок сеансындагы колдонмолорду жана алардагы нерселерди жок кылат"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Чыгуу"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Коноктун аракеттерин сактайсызбы?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Учурдагы сеанстагы аракеттерди сактап же бардык колдонмолорду жана алардагы нерселерди жок кылсаңыз болот"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Өчүрүү"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Сактоо"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Конок режиминен чыгуу"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Конок сеансын кайра коюу"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Конок режиминен чыгуу"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Чыксаңыз, бардык аракеттер өчүрүлөт"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Чыгуудан мурун аракеттериңизди сактап же жок кылсаңыз болот"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Сеанстагы аракеттерди азыр өчүрсөңүз болот же чыгып баратып өчүрүп же сактап коюңуз"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Сүрөт тандаңыз"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index 44621372bff4..74acbc81f606 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ກຳລັງສາກໄຟດ່ວນ"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ກຳລັງສາກໄຟຊ້າໆ"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ກຳລັງສາກໄຟໄຮ້ສາຍ"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ກຳລັງສາກໄຟຜ່ານດັອກ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ບໍ່ໄດ້ສາກໄຟ"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ເຊື່ອມຕໍ່ແລ້ວ, ບໍ່ໄດ້ສາກໄຟ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ສາກເຕັມແລ້ວ"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ຣີເຊັດ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ລຶບອອກ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ກຳລັງຣີເຊັດແຂກ…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ຣີເຊັດເຊດຊັນຂອງແຂກບໍ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ນີ້ຈະເລີ່ມໄລຍະເວລາຂອງແຂກໃໝ່ ແລະ ລຶບແອັບ ແລະ ຂໍ້ມູນທັງໝົດອອກຈາກເຊດຊັນປັດຈຸບັນ"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ອອກຈາກໂໝດແຂກບໍ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ນີ້ຈະລຶບແອັບ ແລະ ຂໍ້ມູນອອກຈາກເຊດຊັນແຂກປັດຈຸບັນ"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ອອກ"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ບັນທຶກການເຄື່ອນໄຫວແຂກບໍ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ທ່ານສາມາດບັນທຶກການເຄື່ອນໄຫວຈາກເຊດຊັນປັດຈຸບັນ ຫຼື ລຶບແອັບ ແລະ ຂໍ້ມູນທັງໝົດໄດ້"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ລຶບ"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ບັນທຶກ"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ອອກຈາກໂໝດແຂກ"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"ຣີເຊັດເຊດຊັນຂອງແຂກ"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ອອກຈາກແຂກ"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ການເຄື່ອນໄຫວທັງໝົດຈະຖືກລຶບໃນຕອນອອກ"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ທ່ານສາມາດບັນທຶກ ຫຼື ລຶບການເຄື່ອນໄຫວຂອງທ່ານໃນຕອນອອກໄດ້"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ຣີເຊັດເພື່ອລຶບການເຄື່ອນໄຫວເຊດຊັນຕອນນີ້ ຫຼື ທ່ານສາມາດບັນທຶກ ຫຼື ລຶບການເຄື່ອນໄຫວໃນຕອນອອກໄດ້"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ເລືອກຮູບ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index b3866dced3e2..13eb79255918 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Greitai įkraunama"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lėtai įkraunama"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kraunama be laidų"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Įkrovimo dokas"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nekraunama"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Prijungta, neįkraunama"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Įkrauta"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Nustatyti iš naujo"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Pašalinti"</string> <string name="guest_resetting" msgid="7822120170191509566">"Svečias nustatomas iš naujo…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Nustatyti svečio sesiją iš naujo?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bus pradėta nauja svečio sesija ir iš esamos sesijos bus ištrintos visos programos ir duomenys"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Išeiti iš svečio režimo?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bus ištrintos esamos svečio sesijos programos ir duomenys"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Išeiti"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Išsaugoti svečio veiklą?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Galite išsaugoti esamos sesijos veiklą arba ištrinti visas programas ir duomenis"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Ištrinti"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Išsaugoti"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Išeiti iš svečio režimo"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Nustatyti svečio sesiją iš naujo"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Išeiti iš svečio režimo"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Išėjus iš režimo visa veikla bus ištrinta"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Išeidami galite išsaugoti arba ištrinti savo veiklą"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Nustatykite iš naujo, jei norite ištrinti sesijos veiklą dabar, arba galite išeidami išsaugoti ar ištrinti veiklą"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Pasirinkti nuotrauką"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index 1fa6bc8ec1c0..1a9a6c380434 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Notiek ātrā uzlāde"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Notiek lēnā uzlāde"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Bezvadu uzlāde"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Uzlādes doks"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenotiek uzlāde"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ierīce pievienota, uzlāde nenotiek"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Uzlādēts"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Atiestatīt"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Noņemt"</string> <string name="guest_resetting" msgid="7822120170191509566">"Notiek viesa sesijas atiestatīšana…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vai atiestatīt viesa sesiju?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tādējādi tiks sākta jauna viesa sesijas un visas pašreizējās sesijas lietotnes un dati tiks dzēsti"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vai iziet no viesa režīma?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tādējādi tiks dzēstas pašreizējās viesa sesijas lietotnes un dati."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Iziet"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vai saglabāt viesa darbības?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Varat saglabāt pašreizējās sesijas darbības vai dzēst visas lietotnes un datus"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Dzēst"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Saglabāt"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Iziet no viesa režīma"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Atiestatīt viesa sesiju"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Iziet no viesa režīma"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Izejot tiks dzēstas visas darbības"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Izejot varat saglabāt vai dzēst savas darbības"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Atiestatiet, lai tagad dzēstu sesijas darbības. Izejot varēsiet saglabāt vai dzēst darbības."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Atlasīt fotoattēlu"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 43fe5e4318cf..98fd0043c0d1 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо полнење"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Бавно полнење"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Се полни безжично"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Се полни на док"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не се полни"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Поврзано, не се полни"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Полна"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ресетирај"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Отстрани"</string> <string name="guest_resetting" msgid="7822120170191509566">"Се ресетира гостинот…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Да се ресетира гостинската сесија?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Ова ќе започне нова гостинска сесија и ќе ги избрише сите апликации и податоци од тековната сесија"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Да се излезе од режим на гостин?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ова ќе ги избрише сите апликации и податоци од тековната гостинска сесија"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Излези"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Да се зачува активност на гостин?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Може да зачувате активност од тековната сесија или да ги избришете сите апликации и податоци"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Избриши"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Зачувај"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Излези од режим на гостин"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Ресетирај ја гостинската сесија"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Излези од режим на гостин"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Целата активност ќе се избрише при излегувањето"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Можете да ја зачувате или избришете вашата активност при излегувањето"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Ресетирајте за да ја избришете активноста на сесијата сега или може да ја зачувате или избришете активноста при излегувањето"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Изберете фотографија"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 64bf8f73a011..5c4cbf36c9ec 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"അതിവേഗ ചാർജിംഗ്"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"പതുക്കെയുള്ള ചാർജിംഗ്"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"വയർലെസായി ചാർജുചെയ്യുന്നു"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ചാർജിംഗ് ഡോക്ക്"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ചാർജ്ജുചെയ്യുന്നില്ല"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"കണക്റ്റ് ചെയ്തിരിക്കുന്നു, ചാർജ് ചെയ്യുന്നില്ല"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ചാർജായി"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"റീസെറ്റ് ചെയ്യുക"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"നീക്കം ചെയ്യുക"</string> <string name="guest_resetting" msgid="7822120170191509566">"അതിഥിയെ റീസെറ്റ് ചെയ്യുന്നു…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"അതിഥി സെഷൻ റീസെറ്റ് ചെയ്യണോ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ഇത് പുതിയൊരു അതിഥി സെഷൻ ആരംഭിക്കുകയും നിലവിലെ സെഷനിൽ നിന്ന് എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കുകയും ചെയ്യും"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"അതിഥി മോഡിൽ നിന്ന് പുറത്തുകടക്കണോ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"നിലവിലെ അതിഥി സെഷനിൽ നിന്ന് ആപ്പുകളും ഡാറ്റയും ഇത് ഇല്ലാതാക്കും"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"പുറത്തുകടക്കുക"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"അതിഥി ആക്റ്റിവിറ്റി സംരക്ഷിക്കണോ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"നിലവിലെ സെഷനിൽ നിന്നുള്ള ആക്റ്റിവിറ്റി സംരക്ഷിക്കാം അല്ലെങ്കിൽ എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കാം"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ഇല്ലാതാക്കുക"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"സംരക്ഷിക്കുക"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"പുറത്തുകടക്കുക"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"അതിഥി സെഷൻ റീസെറ്റ് ചെയ്യുക"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"അതിഥി മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"പുറത്തുകടക്കുമ്പോൾ എല്ലാ ആക്റ്റിവിറ്റിയും ഇല്ലാതാക്കും"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"പുറത്തുകടക്കുമ്പോൾ ആക്റ്റിവിറ്റി സംരക്ഷിക്കുകയോ ഇല്ലാതാക്കുകയോ ചെയ്യാം"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"സെഷൻ ആക്റ്റിവിറ്റി ഇപ്പോൾ ഇല്ലാതാക്കാൻ റീസെറ്റ് ചെയ്യുക അല്ലെങ്കിൽ പുറത്തുകടക്കുമ്പോൾ ആക്റ്റിവിറ്റി സംരക്ഷിക്കുകയോ ഇല്ലാതാക്കുകയോ ചെയ്യാം"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ഫോട്ടോ തിരഞ്ഞെടുക്കുക"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 5dc88cd3637a..475178ef726b 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Удаан цэнэглэж байна"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Утасгүй цэнэглэж байна"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Цэнэглэх холбогч"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Цэнэглэхгүй байна"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Холбогдсон, цэнэглээгүй байна"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Цэнэглэсэн"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Шинэчлэх"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Хасах"</string> <string name="guest_resetting" msgid="7822120170191509566">"Зочныг шинэчилж байна…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Зочны харилцан үйлдлийг шинэчлэх үү?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Энэ нь шинэ зочны харилцан үйлдэл эхлүүлж, одоогийн харилцан үйлдлээс бүх апп болон өгөгдлийг устгана"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Зочны горимоос гарах уу?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Энэ нь одоогийн зочны харилцан үйлдлээс аппууд болон өгөгдлийг устгана"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Гарах"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Зочны үйл ажиллагааг хадгалах уу?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Та одоогийн харилцан үйлдлээс үйл ажиллагаа хадгалах эсвэл бүх апп, өгөгдлийг устгаж болно"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Устгах"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Хадгалах"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Зочны горимоос гарах"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Зочны харилцан үйлдлийг шинэчлэх"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Зочны горимоос гарах"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Бүх үйл ажиллагааг гарах үед устгана"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Та гарахдаа үйл ажиллагаагаа хадгалах эсвэл устгах боломжтой"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Харилцан үйлдлийн үйл ажиллагааг одоо устгахын тулд шинэчлэх эсвэл та гарахдаа үйл ажиллагааг хадгалах, устгах боломжтой"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Зураг сонгох"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 1f89b8ff0892..d18a1f9a580a 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"वेगाने चार्ज होत आहे"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"हळू चार्ज होत आहे"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेसने चार्ज होत आहे"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"चार्जिंग डॉक"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज होत नाही"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"कनेक्ट केले, चार्ज होत नाही"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"चार्ज झाली"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रीसेट करा"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"काढून टाका"</string> <string name="guest_resetting" msgid="7822120170191509566">"अतिथीला रीसेट करत आहे…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"अतिथी सत्र रीसेट करायचे का?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"हे नवीन अतिथी सत्र सुरू करेल आणि सध्याच्या सत्रातील सर्व अॅप्स व डेटा हटवेल"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"अतिथी मोडमधून बाहेर पडायचे का?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"हे सध्याच्या अतिथी सत्रातील अॅप्स आणि डेटा हटवेल"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"बाहेर पडा"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"अतिथी अॅक्टिव्हिटी सेव्ह करायची का?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"सध्याच्या सत्रातील अॅक्टिव्हिटी सेव्ह करू किंवा सर्व अॅप्स व डेटा हटवू शकता"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"हटवा"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"सेव्ह करा"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"अतिथी मोडमधून बाहेर पडा"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"अतिथी सत्र रीसेट करा"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"अतिथी मोडमधून बाहेर पडा"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"बाहेर पडल्यावर सर्व अॅक्टिव्हिटी हटवली जाईल"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"बाहेर पडल्यावर तुमची अॅक्टिव्हिटी सेव्ह करू किंवा हटवू शकता"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"सत्र अॅक्टिव्हिटी आता हटवण्यासाठी रीसेट करा किंवा तुम्ही बाहेर पडल्यावर अॅक्टिव्हिटी सेव्ह करू अथवा हटवू शकता"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो निवडा"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 2034af25b130..efdf98983b20 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mengecas dgn cepat"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mengecas perlahan"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Mengecas tanpa wayar"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Dok Pengecasan"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Tidak mengecas"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Bersambung, tidak mengecas"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Sudah dicas"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tetapkan semula"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Alih keluar"</string> <string name="guest_resetting" msgid="7822120170191509566">"Menetapkan semula tetamu…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Tetapkan semula sesi tetamu?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Tindakan ini akan memulakan sesi tetamu baharu dan memadamkan semua apl dan data daripada sesi semasa"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Keluar daripada mod tetamu?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Tindakan ini akan memadamkan apl dan data daripada sesi tetamu semasa"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Keluar"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Simpan aktiviti tetamu?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Anda boleh menyimpan aktiviti daripada sesi semasa atau memadamkan semua apl dan data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Padam"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Simpan"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Keluar daripada mod tetamu"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Tetapkan semula sesi tetamu"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Keluar mod tetamu"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Semua aktiviti akan dipadamkan semasa keluar"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Aktiviti anda boleh disimpan atau dipadam semasa keluar"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Tetapkan semula sesi untuk memadamkan aktiviti sesi sekarang atau anda boleh menyimpan atau memadamkan aktiviti semasa keluar"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index 2514eff82333..c3dac352ad25 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"အမြန် အားသွင်းနေသည်"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"နှေးကွေးစွာ အားသွင်း"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ကြိုးမဲ့ အားသွင်းနေသည်"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"အားသွင်းအထိုင်"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"အားသွင်းမနေပါ"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ချိတ်ဆက်ထားသည်၊ အားသွင်းမနေပါ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"အားသွင်းပြီးပါပြီ"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ပြင်ဆင်သတ်မှတ်ရန်"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ဖယ်ရှားရန်"</string> <string name="guest_resetting" msgid="7822120170191509566">"ဧည့်သည်ကို ပြင်ဆင်သတ်မှတ်နေသည်…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ဧည့်သည် စက်ရှင် ပြင်ဆင်သတ်မှတ်မလား။"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"၎င်းသည် ဧည့်သည် စက်ရှင်အသစ်ကို စတင်မည်ဖြစ်ပြီး လက်ရှိစက်ရှင်မှ အက်ပ်နှင့် ဒေတာအားလုံးကို ဖျက်ပါမည်"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ဧည့်သည်မုဒ်မှ ထွက်မလား။"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"၎င်းသည် လက်ရှိဧည့်သည် စက်ရှင်မှ အက်ပ်နှင့် ဒေတာအားလုံးကို ဖျက်လိုက်ပါမည်"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ထွက်ရန်"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ဧည့်သည်လုပ်ဆောင်ချက် သိမ်းမလား။"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"လက်ရှိစက်ရှင်မှ လုပ်ဆောင်ချက် သိမ်းနိုင်သည် (သို့) အက်ပ်နှင့် ဒေတာအားလုံး ဖျက်နိုင်သည်"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ဖျက်ရန်"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"သိမ်းရန်"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ဧည့်သည်မုဒ်မှ ထွက်ရန်"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"ဧည့်သည် စက်ရှင် ပြင်ဆင်သတ်မှတ်ရန်"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ဧည့်သည့်မှ ထွက်ရန်"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ထွက်သည့်အခါ လုပ်ဆောင်ချက်အားလုံးကို ဖျက်လိုက်မည်"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ထွက်သည့်အခါ လုပ်ဆောင်ချက်ကို သိမ်းနိုင် (သို့) ဖျက်နိုင်သည်"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"စက်ရှင်လုပ်ဆောင်ချက်ကို ယခုဖျက်ရန် ပြင်ဆင်သတ်မှတ်နိုင်သည် (သို့) ထွက်သည့်အခါ သိမ်းနိုင်၊ ဖျက်နိုင်သည်"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ဓာတ်ပုံရွေးရန်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 2f0b46f08261..f6c82f8eaabd 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Lader sakte"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Lader trådløst"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Ladedokk"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Lader ikke"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Tilkoblet, lader ikke"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Ladet"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tilbakestill"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Fjern"</string> <string name="guest_resetting" msgid="7822120170191509566">"Tilbakestiller gjesten …"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vil du tilbakestille gjesteøkten?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Dette starter en ny gjesteøkt og sletter alle apper og data fra den gjeldende økten"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vil du avslutte gjestemodus?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Dette sletter apper og data fra den gjeldende gjesteøkten"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Avslutt"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vil du lagre gjesteaktivitet?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Du kan lagre aktivitet fra den gjeldende økten eller slette alle apper og data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Slett"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Lagre"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Avslutt gjestemodus"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Tilbakestill gjesteøkten"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Avslutt gjesteøkten"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All aktivitet slettes når du avslutter"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Du kan lagre eller slette aktiviteten når du avslutter"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Tilbakestill for å slette øktaktivitet nå, eller du kan lagre eller slette aktivitet når du avslutter"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Velg et bilde"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 34da9a7a5245..5fa4daa8fb9c 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"द्रुत गतिमा चार्ज गरिँदै छ"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ढिलो चार्ज हुँदै छ"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"वायरलेस तरिकाले चार्ज गरिँदै छ"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"डक चार्ज हुँदै छ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"चार्ज भइरहेको छैन"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"कनेक्ट गरिएको छ, चार्ज भइरहेको छैन"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"चार्ज भयो"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"रिसेट गर्नुहोस्"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"हटाउनुहोस्"</string> <string name="guest_resetting" msgid="7822120170191509566">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गरिँदै छ…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"अतिथि सत्र रिसेट गर्ने हो?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"यसो गर्नाले नयाँ अतिथि सत्र सुरु हुने छ र हालको अतिथि सत्रका सबै एप तथा डेटा मेटिने छ"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"अतिथि मोडबाट बाहिरिने हो?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"यसो गर्नाले हालको अतिथि सत्रका एप तथा डेटा मेटिने छ"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"बाहिरिनुहोस्"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"अतिथि सत्रमा गरिएका क्रियाकलाप सेभ गर्ने हो?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"तपाईं हालको सत्रमा गरिएका क्रियाकलाप सेभ गर्न वा सबै एप तथा डेटा मेटाउन सक्नुहुन्छ"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"मेटाउनुहोस्"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"सेभ गर्नुहोस्"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"अतिथि मोडबाट बाहिरिनुहोस्"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"अतिथि सत्र रिसेट गर्नुहोस्"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"अतिथि मोडबाट बाहिरिनुहोस्"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"अतिथि मोडबाट बाहिरिँदा सबै क्रियाकलाप मेटाइने छ"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"तपाईं अतिथि मोडबाट बाहिरिँदा आफ्ना क्रियाकलाप सेभ गर्ने वा मेटाउने विकल्प रोज्न सक्नुहुन्छ"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"यो सत्रमा गरिएका क्रियाकलाप अहिले नै मेटाउन रिसेट गर्नुहोस्, अथवा तपाईं अतिथि मोडबाट बाहिरिँदा आफ्ना क्रियाकलाप सेभ गर्ने वा मेटाउने विकल्प रोज्न सक्नुहुन्छ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो चयन गर्नुहोस्"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index f250b604eff9..360582fb932c 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Snel opladen"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Langzaam opladen"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Draadloos opladen"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Oplaaddock"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Wordt niet opgeladen"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Verbonden, wordt niet opgeladen"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Opgeladen"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetten"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Verwijderen"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gast resetten…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Gastsessie resetten?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Hierdoor wordt een nieuwe gastsessie gestart en worden alle apps en gegevens van de huidige sessie verwijderd"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Gastmodus sluiten?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Hierdoor worden apps en gegevens van de huidige gastsessie verwijderd"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sluiten"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Gastactiviteit opslaan?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Sla activiteit van de huidige sessie op of verwijder alle apps en gegevens"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Verwijderen"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Opslaan"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Gastmodus sluiten"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Gastsessie resetten"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Gastmodus verlaten"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Alle activiteit wordt na het afsluiten verwijderd"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Je kunt je activiteit bij afsluiten opslaan of verwijderen"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Voer een reset uit om de sessie-activiteit nu te verwijderen of verwijder of sla je activiteit op bij afsluiten"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Foto selecteren"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index fa57c63e8ce9..e25124c953b1 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ଶୀଘ୍ର ଚାର୍ଜ ହେଉଛି"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ଧୀରେ ଚାର୍ଜ ହେଉଛି"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ୱେୟରଲେସ ଭାବେ ଚାର୍ଜିଂ"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ଡକ ଚାର୍ଜ ହେଉଛି"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ଚାର୍ଜ ହେଉନାହିଁ"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ସଂଯୋଗ କରାଯାଇଛି, ଚାର୍ଜ ହେଉନାହିଁ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ଚାର୍ଜ ହୋଇଯାଇଛି"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ରିସେଟ୍ କରନ୍ତୁ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"କାଢ଼ି ଦିଅନ୍ତୁ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ଅତିଥି ସେସନକୁ ରିସେଟ୍ କରାଯାଉଛି…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ଅତିଥି ସେସନକୁ ରିସେଟ କରିବେ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ଏହା ଏକ ନୂଆ ଅତିଥି ସେସନ ଆରମ୍ଭ କରିବ ଏବଂ ବର୍ତ୍ତମାନର ସେସନରୁ ସମସ୍ତ ଆପ୍ସ ଏବଂ ଡାଟାକୁ ଡିଲିଟ କରିବ"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ଅତିଥି ମୋଡରୁ ବାହାରି ଯିବେ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ଏହା ବର୍ତ୍ତମାନର ଅତିଥି ସେସନରୁ ଆପ୍ସ ଏବଂ ଡାଟାକୁ ଡିଲିଟ କରିବ"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ବାହାରି ଯାଆନ୍ତୁ"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ଅତିଥି କାର୍ଯ୍ୟକଳାପ ସେଭ କରିବେ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ଆପଣ ଏବେର ସେସନରୁ କାର୍ଯ୍ୟକଳାପକୁ ସେଭ କରିପାରିବେ ବା ସବୁ ଆପ୍ସ ଓ ଡାଟାକୁ ଡିଲିଟ କରିପାରିବେ"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ଡିଲିଟ କରନ୍ତୁ"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ସେଭ କରନ୍ତୁ"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ଅତିଥି ମୋଡରୁ ବାହାରି ଯାଆନ୍ତୁ"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"ଅତିଥି ସେସନକୁ ରିସେଟ କରନ୍ତୁ"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ଅତିଥି ମୋଡରୁ ବାହାରି ଯାଆନ୍ତୁ"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ବାହାରିବା ସମୟରେ ସମସ୍ତ କାର୍ଯ୍ୟକଳାପକୁ ଡିଲିଟ କରାଯିବ"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ବାହାରିବା ସମୟରେ ଆପଣଙ୍କର କାର୍ଯ୍ୟକଳାପକୁ ସେଭ ବା ଡିଲିଟ କରିପାରିବେ"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ବର୍ତ୍ତମାନ ସେସନ କାର୍ଯ୍ୟକଳାପକୁ ଡିଲିଟ କରିବାକୁ ରିସେଟ କରନ୍ତୁ କିମ୍ବା ବାହାରିବା ସମୟରେ ଆପଣ କାର୍ଯ୍ୟକଳାପକୁ ସେଭ କିମ୍ବା ଡିଲିଟ କରିପାରିବେ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ଫଟୋ ବାଛନ୍ତୁ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index 90f2b0fddb9b..ca58fe946b76 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"ਬਿਨਾਂ ਤਾਰ ਤੋਂ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ਡੌਕ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਿਹਾ"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"ਕਨੈਕਟ ਹੈ, ਚਾਰਜ ਨਹੀਂ ਹੋ ਰਹੀ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ਚਾਰਜ ਹੋ ਗਈ"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ਰੀਸੈੱਟ ਕਰੋ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ਹਟਾਓ"</string> <string name="guest_resetting" msgid="7822120170191509566">"ਮਹਿਮਾਨ ਨੂੰ ਰੀਸੈੱਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ਕੀ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਨੂੰ ਰੀਸੈੱਟ ਕਰਨਾ ਹੈ?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ਇਸ ਨਾਲ ਨਵਾਂ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਸ਼ੁਰੂ ਹੋ ਜਾਵੇਗਾ ਅਤੇ ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਦੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟ ਜਾਵੇਗਾ"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ਕੀ ਮਹਿਮਾਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਣਾ ਹੈ?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ਇਸ ਨਾਲ ਮੌਜੂਦਾ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਦੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟ ਜਾਵੇਗਾ"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ਬਾਹਰ ਜਾਓ"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ਕੀ ਮਹਿਮਾਨ ਸਰਗਰਮੀ ਰੱਖਿਅਤ ਕਰਨੀ ਹੈ?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ਤੁਸੀਂ ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਦੀ ਸਰਗਰਮੀ ਨੂੰ ਰੱਖਿਅਤ ਕਰ ਸਕਦੇ ਹੋ ਜਾਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ਮਿਟਾਓ"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"ਰੱਖਿਅਤ ਕਰੋ"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ਮਹਿਮਾਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਨੂੰ ਰੀਸੈੱਟ ਕਰੋ"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ਮਹਿਮਾਨ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ਬਾਹਰ ਜਾਣ \'ਤੇ ਸਾਰੀ ਸਰਗਰਮੀ ਮਿਟਾਈ ਜਾਵੇਗੀ"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ਤੁਸੀਂ ਬਾਹਰ ਜਾਣ \'ਤੇ ਆਪਣੀ ਸਰਗਰਮੀ ਰੱਖਿਅਤ ਕਰ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"ਸੈਸ਼ਨ ਦੀ ਸਰਗਰਮੀ ਹੁਣੇ ਮਿਟਾਉਣ ਲਈ ਰੀਸੈੱਟ ਕਰੋ ਜਾਂ ਤੁਸੀਂ ਬਾਹਰ ਜਾਣ \'ਤੇ ਸਰਗਰਮੀ ਨੂੰ ਰੱਖਿਅਤ ਕਰ ਜਾਂ ਮਿਟਾ ਸਕਦੇ ਹੋ"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ਫ਼ੋਟੋ ਚੁਣੋ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 1eb1b6c870b9..860ee17f4a14 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Wolne ładowanie"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Ładowanie bezprzewodowe"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Ładowanie na stacji dokującej"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nie podłączony"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Podłączono, brak ładowania"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Naładowana"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetuj"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Usuń"</string> <string name="guest_resetting" msgid="7822120170191509566">"Resetuję sesję gościa…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Zresetować sesję gościa?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Zostanie uruchomiona nowa sesja gościa. Wszystkie aplikacje i dane z obecnej sesji zostaną usunięte."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Zamknąć tryb gościa?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Wszystkie aplikacje i dane z obecnej sesji gościa zostaną usunięte."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Wyjdź"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Zapisać aktywność gościa?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Możesz zapisać aktywność z obecnej sesji lub usunąć wszystkie aplikacje i dane"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Usuń"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Zapisz"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Zamknij tryb gościa"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Zresetuj sesję gościa"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Zakończ tryb gościa"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Cała aktywność zostanie usunięta po zamknięciu"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Możesz zapisać lub usunąć swoją aktywność podczas zamykania."</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Zresetuj, aby teraz usunąć aktywność z tej sesji. Możesz też ją zapisać lub usunąć podczas zamykania sesji."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Wybierz zdjęcie"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 8c3f1fad8211..4854709fb614 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregando devagar"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregando sem fio"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de carregamento"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está carregando"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado sem carregar"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Redefinir"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string> <string name="guest_resetting" msgid="7822120170191509566">"Redefinindo visitante…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Redefinir Sessão de visitante?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Essa ação vai iniciar uma nova Sessão de visitante e excluir todos os apps e dados da sessão atual"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sair do modo visitante?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Essa ação vai excluir apps e dados da Sessão de visitante atual"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sair"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvar a atividade do visitante?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Você pode salvar a atividade da sessão atual ou excluir todos os apps e dados"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Excluir"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvar"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Sair do modo visitante"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Redefinir Sessão de visitante"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Sair do modo visitante"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Todas as atividades serão excluídas ao sair"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Você pode salvar ou excluir sua atividade ao sair"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Redefina para excluir a atividade da sessão agora. Salve ou exclua a atividade ao sair"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index ab0da74cba10..2ebf1d71e133 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregamento rápido"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregamento lento"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"A carregar sem fios"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Est. ancor. carreg."</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está a carregar"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ligado, não está a carregar"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Repor"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string> <string name="guest_resetting" msgid="7822120170191509566">"A repor o convidado…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Repor sessão de convidado?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Esta ação inicia uma nova sessão de convidado e elimina todas as apps e dados da sessão atual"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sair do modo convidado?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Esta ação elimina as apps e os dados da sessão de convidado atual"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sair"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Guardar atividade de convidado?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Pode guardar a atividade da sessão atual ou eliminar todas as apps e dados"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Eliminar"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Guardar"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Sair do modo convidado"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Repor sessão de convidado"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Sair do modo de convidado"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toda a atividade é eliminada ao sair"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Pode guardar ou eliminar a sua atividade ao sair"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Reponha para eliminar agora a atividade da sessão. Pode ainda guardar ou eliminar a atividade ao sair"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 8c3f1fad8211..4854709fb614 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregando rápido"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Carregando devagar"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Carregando sem fio"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Base de carregamento"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Não está carregando"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectado sem carregar"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Carregada"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Redefinir"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Remover"</string> <string name="guest_resetting" msgid="7822120170191509566">"Redefinindo visitante…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Redefinir Sessão de visitante?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Essa ação vai iniciar uma nova Sessão de visitante e excluir todos os apps e dados da sessão atual"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Sair do modo visitante?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Essa ação vai excluir apps e dados da Sessão de visitante atual"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Sair"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvar a atividade do visitante?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Você pode salvar a atividade da sessão atual ou excluir todos os apps e dados"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Excluir"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvar"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Sair do modo visitante"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Redefinir Sessão de visitante"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Sair do modo visitante"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Todas as atividades serão excluídas ao sair"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Você pode salvar ou excluir sua atividade ao sair"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Redefina para excluir a atividade da sessão agora. Salve ou exclua a atividade ao sair"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 4f142a81f472..3dd56eb2b311 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Se încarcă rapid"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Se încarcă lent"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Se încarcă wireless"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Suport de încărcare"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nu se încarcă"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Conectat, nu se încarcă"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Încărcată"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetați"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Eliminați"</string> <string name="guest_resetting" msgid="7822120170191509566">"Se resetează invitatul…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Resetați sesiunea pentru invitați?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Astfel, va începe o nouă sesiune pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea actuală"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Ieșiți din modul pentru invitați?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ieșiți"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvați activitatea invitatului?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Salvați activitatea din sesiunea actuală sau ștergeți aplicațiile și datele"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Ștergeți"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Salvați"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Ieșiți din modul pentru invitați"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Resetați sesiunea pentru invitați"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ieșiți din modul pentru invitați"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toate activitățile vor fi șterse la ieșire"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puteți să salvați sau să ștergeți activitatea la ieșire"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetați pentru a șterge acum activitatea din sesiune sau salvați ori ștergeți activitatea la ieșire"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index d70e31a21c0b..68543a03811f 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Быстрая зарядка"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Медленная зарядка"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Беспроводная зарядка"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Док-станция: зарядка"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не заряжается"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Подключено, не заряжается"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Батарея заряжена"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Сбросить"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Удалить"</string> <string name="guest_resetting" msgid="7822120170191509566">"Сброс гостевого сеанса…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Сбросить гостевой сеанс?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"При этом начнется новый гостевой сеанс, а все данные и приложения предыдущего сеанса будут удалены."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Выйти из гостевого режима?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Все приложения и данные текущего гостевого сеанса будут удалены."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Выйти"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Сохранить историю сеанса?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Сохраните историю текущего сеанса или удалите данные и приложения."</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Удалить"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Сохранить"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Выйти из гостевого режима"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Сбросить гостевой сеанс"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Выйти из гостевого режима"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"История будет удалена сразу после выхода."</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"При выходе вы можете сохранить или удалить историю."</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Можно сбросить историю сеанса прямо сейчас, либо удалить или сохранить ее при выходе."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Выбрать фотографию"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 217ba1de6812..dbc1fc2a89c4 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්ර ආරෝපණය"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"සෙමින් ආරෝපණය"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"නොරැහැන්ව ආරෝපණය වේ"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ආරෝපණ ඩොකය"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ආරෝපණය නොවේ"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"සම්බන්ධයි, ආරෝපණය නොවේ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"අරෝපිතයි"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"යළි සකසන්න"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ඉවත් කරන්න"</string> <string name="guest_resetting" msgid="7822120170191509566">"අමුත්තා යළි සකසමින්…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"ආගන්තුක සැසිය යළි සකසන්නද?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"මෙය නව ආගන්තුක සැසියක් ආරම්භ කර වත්මන් සැසියෙන් සියලු යෙදුම් සහ දත්ත මකනු ඇත"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ආගන්තුක ප්රකාරයෙන් පිටවන්නද?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"මෙය වත්මන් ආගන්තුක සැසියෙන් යෙදුම් සහ දත්ත මකනු ඇත"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"පිටවන්න"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"ආගන්තුක ක්රියාකාරකම් සුරකින්නද?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"ඔබට වත්මන් සැසියෙන් ක්රියාකාරකම් සුරැකීමට හෝ සියලු යෙදුම් සහ දත්ත මැකීමට හැකිය"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"මකන්න"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"සුරකින්න"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ආගන්තුක ප්රකාරයෙන් පිටවන්න"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"ආගන්තුක සැසිය යළි සකසන්න"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"අමුත්තා පිටවීම"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"පිටවීමේදී සියලු ක්රියාකාරකම් මකනු ඇත"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"ඔබට පිටවීමේදී ඔබගේ ක්රියාකාරකම් සුරැකීමට හෝ මැකීමට හැකිය"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"දැන් සැසි ක්රියාකාරකම් මැකීමට යළි සකසන්න, නැතහොත් ඔබට පිටවීමේදී ක්රියාකාරකම් සුරැකීමට හෝ මැකීමට හැකිය"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ඡායාරූපය තෝරන්න"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 51588e33a97e..a3a0e2307fb1 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Rýchle nabíjanie"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Pomalé nabíjanie"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Nabíja sa bezdrôtovo"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Nabíjací dok"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nenabíja sa"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Pripojené, nenabíja sa"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Nabité"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Resetovať"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstrániť"</string> <string name="guest_resetting" msgid="7822120170191509566">"Relácia hosťa sa resetuje…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Chcete resetovať reláciu hosťa?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Týmto sa spustí nová relácia hosťa a odstránia sa všetky aplikácie a údaje z aktuálnej relácie"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Chcete ukončiť režim pre hostí?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ukončí sa režim pre hostí a odstránia sa aplikácie a údaje z relácie hosťa"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ukončiť"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Chcete uložiť aktivitu hosťa?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Môžte uložiť aktivitu aktuálnej relácie alebo odstrániť všetky aplikácie a údaje"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Odstrániť"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Uložiť"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Ukončiť režim pre hostí"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Resetovať reláciu hosťa"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ukončiť režim pre hostí"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Pri ukončení sa všetka aktivita odstráni"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Aktivitu môžete pri ukončení uložiť alebo odstrániť"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetovaním ihneď odstránite aktivitu relácie alebo ju uložte či odstráňte pri ukončení relácie"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrať fotku"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index d1c355849fe5..7e2172fb65fe 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Počasno polnjenje"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Brezžično polnjenje"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Polnjenje na nosilcu"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Se ne polni"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Povezano, se ne polni"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Napolnjeno"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ponastavi"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Odstrani"</string> <string name="guest_resetting" msgid="7822120170191509566">"Ponastavljanje gosta …"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Želite ponastaviti sejo gosta?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"S tem boste začeli novo sejo gosta ter izbrisali vse aplikacije in podatke v trenutni seji."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Želite zapreti način za goste?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"S tem boste izbrisali aplikacije in podatke v trenutni seji gosta."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Zapri"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Želite shraniti dejavnost gosta?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Lahko shranite dejavnost v trenutni seji ali izbrišete vse aplikacije in podatke."</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Izbriši"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Shrani"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Zapri način za goste"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Ponastavi sejo gosta"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Zapri sejo gosta"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Ko zaprete način za goste, bo vsa dejavnost izbrisana."</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Ob zaprtju načina lahko shranite ali izbrišete dejavnost."</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Ponastavite za izbris dejavnosti v seji zdaj, lahko pa jo shranite ali izbrišete, ko zaprete način za goste."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Izbira fotografije"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index b5dfc5f43238..146471d1fe95 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Karikim i shpejtë"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Po karikohet ngadalë"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Po karikohet pa tel"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Në stacion karikimi"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Nuk po karikohet"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Lidhur, jo në karikim"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Karikuar"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Rivendos"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Hiq"</string> <string name="guest_resetting" msgid="7822120170191509566">"Vizitori po rivendoset…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Të rivendoset sesioni për vizitorë?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Kjo do të fillojë një sesion të ri për vizitorë dhe do të fshijë të gjitha aplikacionet dhe të dhënat nga sesioni aktual"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Të hiqet modaliteti \"vizitor\"?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Kjo do të fshijë aplikacionet dhe të dhënat nga sesioni aktual për vizitorë"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Dil"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Të ruhet aktiviteti i vizitorit?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Ruaj aktivitetin nga sesioni aktual ose fshi të gjitha aplikacionet e të dhënat"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Fshi"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Ruaj"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Dil nga modaliteti \"vizitor\""</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Rivendos sesionin për vizitorë"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Dil nga modaliteti \"vizitor\""</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Të gjitha aktivitetet do të fshihen kur të dalësh"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Mund ta ruash ose ta fshish aktivitetin tënd kur të dalësh"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Rivendose për të fshirë aktivitetin e sesionit tani ose mund ta ruash ose ta fshish aktivitetin kur të dalësh"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Zgjidh një fotografi"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 4d8a11f68389..e429a635ce7f 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Споро се пуни"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бежично пуњење"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Станица за пуњење"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не пуни се"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Повезано, не пуни се"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Напуњено"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Ресетуј"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Уклони"</string> <string name="guest_resetting" msgid="7822120170191509566">"Сесија госта се ресетује…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Желите да ресетујете сесију госта?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Тиме ћете покренути нову сесију госта и избрисати све апликације и податке из актуелне сесије"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Изаћи ћете из режима госта?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Тиме ћете избрисати све апликације и податке из актуелне сесије госта"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Изађи"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Сачуваћете активности госта?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Сачувајте активности из актуелне сесије или избришите све апликације и податке"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Избриши"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Сачувај"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Изађи из режима госта"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Ресетуј сесију госта"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Изађи из режима госта"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Све активности ће бити избрисане при излазу"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Можете да сачувате или избришете активности при излазу"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Ресетујете за брисање активности сесије, или сачувајте или избришите активности при излазу"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Изаберите слику"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 6e2ea780b24f..c92356ba794c 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Laddas snabbt"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Laddas långsamt"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Laddas trådlöst"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Dockningsstation"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Laddar inte"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ansluten, laddas inte"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Laddat"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Återställ"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ta bort"</string> <string name="guest_resetting" msgid="7822120170191509566">"Gästsessionen återställs …"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Vill du återställa gästsessionen?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"En ny gästsession startas och alla appar och all data från den pågående sessionen raderas"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Vill du avsluta gästläget?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Appar och data från den pågående gästsessionen raderas"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Avsluta"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Vill du spara gästaktivitet?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Du kan spara aktivitet från den pågående sessionen eller radera appar och data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Radera"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Spara"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Avsluta gästläget"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Återställ gästsession"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Avsluta gästsession"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"All aktivitet raderas när du avslutar"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Du kan spara eller radera aktivitet när du avslutar"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Återställ om du vill radera sessionsaktiviteten nu, eller spara eller radera aktivitet när du avslutar"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Välj foto"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 36509e9a63ab..ee66c7346a52 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Inachaji kwa kasi"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Inachaji pole pole"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Inachaji bila kutumia waya"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Kituo cha Kuchaji"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Haichaji"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Imeunganishwa, haichaji"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Imechajiwa"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Badilisha"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Ondoa"</string> <string name="guest_resetting" msgid="7822120170191509566">"Inabadilisha kipindi cha mgeni…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Ungependa kuweka upya kipindi cha mgeni?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Hii itaanzisha upya kipindi cha mgeni na kufuta programu na data yote kwenye kipindi cha sasa"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Utafunga matumizi ya wageni?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Hatua hii itafuta programu na data kutoka kwenye kipindi cha mgeni cha sasa"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Funga"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Utahifadhi shughuli za mgeni?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Unaweza kuhifadhi shughuli kutoka kipindi cha sasa au kufuta programu na data yote"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Futa"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Hifadhi"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Funga matumizi ya wageni"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Weka upya kipindi cha mgeni"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Funga utumiaji wa mgeni"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Shughuli zote zitafutwa wakati wa kufunga"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Unaweza kuhifadhi au kufuta shughuli zako wakati wa kufunga"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Weka upya ili ufute shughuli za kipindi sasa au unaweza kuhifadhi au kufuta shughuli wakati wa kufunga"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Chagua picha"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 6d4c57955b2f..1c60acc19aff 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"வேகமாக சார்ஜாகிறது"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"மெதுவாக சார்ஜாகிறது"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"வயரின்றி சார்ஜாகிறது"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"சார்ஜிங் டாக்"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"சார்ஜ் செய்யப்படவில்லை"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"இணைக்கப்பட்டுள்ளது, சார்ஜாகவில்லை"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"சார்ஜாகிவிட்டது"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"மீட்டமை"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"அகற்று"</string> <string name="guest_resetting" msgid="7822120170191509566">"கெஸ்ட்டை மீட்டமைக்கிறது…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"கெஸ்ட் அமர்வை ரீசெட் செய்யவா?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"புதிய கெஸ்ட் அமர்வு தொடங்கப்படும், மேலும் தற்போதைய கெஸ்ட் அமர்வின் ஆப்ஸ் மற்றும் தரவு அனைத்தும் நீக்கப்படும்"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"கெஸ்ட் முறையிலிருந்து வெளியேறவா?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"தற்போதைய கெஸ்ட் அமர்வின் ஆப்ஸ் மற்றும் தரவு அனைத்தும் நீக்கப்படும்"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"வெளியேறு"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"கெஸ்ட் செயல்பாடுகளைச் சேமிக்கவா?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"தற்போதைய அமர்வின் செயல்பாடுகளைச் சேமிக்கலாம் அல்லது ஆப்ஸையும் தரவையும் நீக்கலாம்"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"நீக்கு"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"சேமி"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"கெஸ்ட் பயன்முறையிலிருந்து வெளியேறு"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"கெஸ்ட் அமர்வை ரீசெட் செய்"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"கெஸ்ட் பயன்முறையிலிருந்து வெளியேறு"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"வெளியேறியவுடன் அனைத்துச் செயல்பாடுகளும் நீக்கப்படும்"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"வெளியேறும்போது செயல்பாடுகளைச் சேமிக்கலாம் அல்லது நீக்கலாம்"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"அமர்வின் செயல்பாடுகளை இப்போதே நீக்க ரீசெட் செய்யவும் அல்லது வெளியேறும்போது செயல்பாடுகளைச் சேமிக்கலாம் அல்லது நீக்கலாம்"</string> <string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"படத்தைத் தேர்ந்தெடுங்கள்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 5e6fa8e24b92..5963495534fc 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"నెమ్మదిగా ఛార్జింగ్"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"వైర్లెస్ ఛార్జింగ్"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"ఛార్జింగ్ డాక్"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ఛార్జ్ కావడం లేదు"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"కనెక్ట్ చేయబడింది, ఛార్జ్ చేయబడలేదు"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ఛార్జ్ చేయబడింది"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"రీసెట్ చేయండి"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"తీసివేయండి"</string> <string name="guest_resetting" msgid="7822120170191509566">"గెస్ట్ సెషన్ను రీసెట్ చేస్తోంది…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"గెస్ట్ సెషన్ను రీసెట్ చేయాలా?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"ఇది కొత్త గెస్ట్ సెషన్ను ప్రారంభిస్తుంది, ప్రస్తుత సెషన్ నుండి అన్ని యాప్లు, డేటాను తొలగిస్తుంది."</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"గెస్ట్ మోడ్ నిష్క్రమించాలా?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"ఇది ప్రస్తుత గెస్ట్ సెషన్ నుండి యాప్లను వాటితో పాటు డేటాను తొలగిస్తుంది"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"నిష్క్రమించండి"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"గెస్ట్ యాక్టివిటీని సేవ్ చేయాలా?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"మీరు సెషన్ నుండి యాక్టివిటీని సేవ్ చేయవచ్చు, అన్ని యాప్లు, డేటాను తొలగించవచ్చు"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"తొలగించండి"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"సేవ్ చేయండి"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"గెస్ట్ మోడ్ నుండి వైదొలగండి"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"గెస్ట్ సెషన్ను రీసెట్ చేయండి"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"గెస్ట్ మోడ్ నుండి నిష్క్రమించండి"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"నిష్క్రమణ సమయంలో మొత్తం యాక్టివిటీ తొలగించబడుతుంది"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"మీ నిష్క్రమణలో, యాక్టివిటీని సేవ్ చేయవచ్చు లేదా తొలగించవచ్చు"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"సెషన్ యాక్టివిటీని తొలగించడానికి ఇప్పుడే రీసెట్ చేయండి లేదా మీరు నిష్క్రమించేటప్పుడు యాక్టివిటీని సేవ్ చేయవచ్చు లేదా తొలగించవచ్చు"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్ను ఎంచుకోండి"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"ఫోటోను ఎంచుకోండి"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 772a9795ba1d..e46bdc481a00 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"กำลังชาร์จอย่างเร็ว"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"กำลังชาร์จอย่างช้าๆ"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"กำลังชาร์จแบบไร้สาย"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"กำลังชาร์จบนแท่น"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"ไม่ได้ชาร์จ"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"เชื่อมต่ออยู่ ไม่ได้ชาร์จ"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"ชาร์จแล้ว"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"รีเซ็ต"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"นำออก"</string> <string name="guest_resetting" msgid="7822120170191509566">"กำลังรีเซ็ตผู้เข้าร่วม…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"รีเซ็ตเซสชันผู้ใช้ชั่วคราวไหม"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"การดำเนินการนี้จะเริ่มเซสชันผู้ใช้ชั่วคราวใหม่ และจะลบแอปและข้อมูลทั้งหมดจากเซสชันปัจจุบัน"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"ออกจากโหมดผู้ใช้ชั่วคราวไหม"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"การดำเนินการนี้จะลบแอปและข้อมูลออกจากเซสชันผู้ใช้ชั่วคราวในปัจจุบัน"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"ออก"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"บันทึกกิจกรรมของผู้ใช้ชั่วคราวไหม"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"คุณสามารถบันทึกกิจกรรมจากเซสชันปัจจุบันหรือจะลบแอปและข้อมูลทั้งหมดก็ได้"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"ลบ"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"บันทึก"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"ออกจากโหมดผู้ใช้ชั่วคราว"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"รีเซ็ตเซสชันผู้ใช้ชั่วคราว"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"ออกจากโหมดผู้ใช้ชั่วคราว"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"ระบบจะลบกิจกรรมทั้งหมดเมื่อออก"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"คุณสามารถบันทึกหรือลบกิจกรรมเมื่อออก"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"รีเซ็ตเพื่อลบกิจกรรมของเซสชันตอนนี้เลย หรือจะบันทึกหรือลบกิจกรรมเมื่อออกก็ได้"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"เลือกรูปภาพ"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index d9d4e3a1117b..21d07c1a314a 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Mabilis na charge"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Mabagal na charge"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Wireless na nagcha-charge"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Charging Dock"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Hindi nagcha-charge"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Nakakonekta, hindi nagcha-charge"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Nasingil"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"I-reset"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Alisin"</string> <string name="guest_resetting" msgid="7822120170191509566">"Nire-reset ang bisita…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"I-reset ang session ng bisita?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Magsisimula ito ng bagong session ng bisita at made-delete ang lahat ng app at data mula sa kasalukuyang session"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Umalis sa guest mode?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Ide-delete nito ang mga app at data mula sa kasalukuyang session ng bisita"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Umalis"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"I-save ang aktibidad ng bisita?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Puwedeng i-save ang aktibidad ng session ngayon o i-delete lahat ng app at data"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"I-delete"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"I-save"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Umalis sa guest mode"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"I-reset ang session ng bisita"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Umalis sa pagiging bisita"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Made-delete ang lahat ng aktibidad kapag umalis"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puwede mong i-save o i-delete ang aktibidad pagkaalis"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Mag-reset para mag-delete ng aktibidad ng session ngayon, o puwede kang mag-save o mag-delete ng aktibidad pagkaalis"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Pumili ng larawan"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index c47e8eea521a..0a5f7da5a00d 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hızlı şarj oluyor"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Yavaş şarj oluyor"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Kablosuz şarj oluyor"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Şarj Yuvası"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Şarj olmuyor"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Bağlandı, şarj olmuyor"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Şarj oldu"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Sıfırla"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Kaldır"</string> <string name="guest_resetting" msgid="7822120170191509566">"Misafir oturumu sıfırlanıyor…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Misafir oturumu sıfırlansın mı?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bu işlem, yeni bir misafir oturumu başlatarak mevcut oturumdaki tüm uygulamaları ve verileri siler"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Misafir modundan çıkılsın mı?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bu işlem mevcut misafir oturumundaki tüm uygulamaları ve verileri siler"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Çık"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Misafir etkinliği kaydedilsin mi?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Oturumdaki etkinliği kaydedebilir ya da tüm uygulama ve verileri silebilirsiniz"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Sil"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Kaydet"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Misafir modundan çık"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Misafir oturumunu sıfırla"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Misafir modundan çık"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Çıkış yapıldığında tüm etkinlikler silinir"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Etkinliklerinizi çıkarken kaydedebilir veya silebilirsiniz"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Oturum etkinliklerini silmek için sıfırlayabilir ya da çıkarken kaydedebilir veya silebilirsiniz"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Fotoğraf seç"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 00c2153cb4b8..769aab78a995 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Повільне заряджання"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Бездротове заряджання"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Зарядка: док-станція"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Не заряджається"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Підключено, не заряджається"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Заряджено"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Скинути"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Вилучити"</string> <string name="guest_resetting" msgid="7822120170191509566">"Скидання сеансу в режимі \"Гість\"…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Скинути сеанс у режимі гостя?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Почнеться новий сеанс у режимі гостя, а всі додатки й дані з поточного сеансу буде видалено"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Вийти з режиму гостя?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Усі додатки й дані з поточного сеансу в режимі гостя буде видалено."</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Вийти"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Зберегти дії в режимі гостя?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Ви можете зберегти дії з поточного сеансу або видалити всі додатки й дані"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Видалити"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Зберегти"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Вийти з режиму гостя"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Скинути сеанс у режимі гостя"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Вийти з режиму гостя"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Під час виходу буде видалено історію всіх дій"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Під час виходу можна зберегти або видалити ваші дії"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Можна скинути історію сеансу просто зараз або видалити чи зберегти її під час виходу."</string> <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Вибрати фотографію"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 43dee94bd9a6..5d7928bfe9e9 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"تیزی سے چارج ہو رہا ہے"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"آہستہ چارج ہو رہی ہے"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"وائرلیس طریقے سے چارج ہو رہی ہے"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"چارجنگ ڈاک"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"چارج نہیں ہو رہا ہے"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"منسلک ہے، چارج نہیں ہو رہی ہے"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"چارج ہو گئی"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"ری سیٹ کریں"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"ہٹائیں"</string> <string name="guest_resetting" msgid="7822120170191509566">"مہمان کو ری سیٹ کرنا…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"مہمان سیشن کو ری سیٹ کریں؟"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"اس سے ایک نیا مہمان سیشن شروع ہو گا اور موجودہ سیشن سے تمام ایپس اور ڈیٹا حذف ہو جائے گا"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"مہمان وضع سے باہر نکلیں؟"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"یہ موجودہ مہمان سیشن سے ایپس اور ڈیٹا کو حذف کر دے گا"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"باہر نکلیں"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"مہمان کی سرگرمی محفوظ کریں؟"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"آپ موجودہ سیشن سے سرگرمی کو محفوظ یا تمام ایپس اور ڈیٹا کو حذف کر سکتے ہیں"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"حذف کریں"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"محفوظ کریں"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"مہمان وضع سے باہر نکلیں"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"مہمان سیشن کو ری سیٹ کریں"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"مہمان وضع سے باہر نکلیں"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"باہر نکلنے پر تمام سرگرمیاں حذف کر دی جائیں گی"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"باہر نکلنے پر آپ اپنی سرگرمی کو محفوظ یا حذف کر سکتے ہیں"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"سیشن کی سرگرمی کو ابھی حذف کرنے کے لیے ری سیٹ کریں، یا باہر نکلنے پر آپ اپنی سرگرمی کو محفوظ یا حذف کر سکتے ہیں"</string> <string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"تصویر منتخب کریں"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 22251c3c186d..46cd59690f0b 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Sekin quvvat olmoqda"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Simsiz quvvat olmoqda"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Quvvatlash doki"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Quvvat olmayapti"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ulangan, quvvat olmayapti"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Quvvat oldi"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Tiklash"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Olib tashlash"</string> <string name="guest_resetting" msgid="7822120170191509566">"Mehmon seansi tiklanmoqda…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Mehmon seansi tiklansinmi?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Bunda yangi mehmon seansi ishga tushadi va joriy seans ilova va maʼlumotlari tozalanadi"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Mehmon rejimidan chiqasizmi?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Bunda joriy mehmon seansidagi ilova va ularning maʼlumotlari tozalanadi"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Chiqish"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Mehmon faoliyati saqlansinmi?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Joriy seansdagi faoliyatni saqlash yoki barcha ilova va maʼlumotlarni oʻchirib tashlashingiz mumkin"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Oʻchirish"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Saqlash"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Mehmon rejimidan chiqish"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Mehmon seansini tiklash"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Mehmon rejimidan chiqish"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Chiqishda faolliklar tarixi tozalab tashlanadi"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Chiqish vaqtida faoliyatni saqlash yoki tozalash mumkin"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Faoliyat hozir tozalanib tiklanishi yoki chiqish vaqtida saqlanishi yoki tozalanishi mumkin"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Surat tanlash"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 2be2b5c9bc15..e11fefd2d923 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Đang sạc chậm"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Đang sạc không dây"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Đế sạc"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Hiện không sạc"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Đã kết nối nhưng chưa sạc"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Đã sạc"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Đặt lại"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Xoá"</string> <string name="guest_resetting" msgid="7822120170191509566">"Đang đặt lại phiên khách…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Đặt lại phiên khách?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Thao tác này sẽ bắt đầu một phiên khách mới và xoá mọi ứng dụng cũng như dữ liệu trong phiên hiện tại"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Thoát khỏi chế độ khách?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Thao tác này sẽ xoá các ứng dụng và dữ liệu trong phiên khách hiện tại"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Thoát"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Lưu hoạt động ở chế độ khách?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Bạn có thể lưu hoạt động trong phiên hiện tại hoặc xoá mọi ứng dụng và dữ liệu"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Xoá"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Lưu"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Thoát khỏi chế độ khách"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Đặt lại phiên khách"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Thoát khỏi chế độ khách"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Mọi hoạt động sẽ bị xoá khi thoát"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Bạn có thể lưu hoặc xoá hoạt động của mình khi thoát"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Đặt lại để xoá hoạt động trong phiên ngay bây giờ, hoặc bạn có thể lưu hoặc xoá hoạt động khi thoát"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Chọn ảnh"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index e5dd6a014ddc..1a44ac25bbad 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充电"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"正在慢速充电"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"正在无线充电"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"充电基座"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"未在充电"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"已连接,未充电"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"已充满电"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重置"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string> <string name="guest_resetting" msgid="7822120170191509566">"正在重置访客会话…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"要重置访客会话吗?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"此操作会开始新的访客会话,并删除当前会话中的所有应用和数据"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"要退出访客模式吗?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"此操作会删除当前访客会话中的所有应用和数据"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"退出"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"要保存访客活动记录吗?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"您可以保存当前会话中的活动记录,也可以删除所有应用和数据"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"删除"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"保存"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"退出访客模式"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"重置访客会话"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"退出访客模式"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"退出时所有活动记录都将被删除"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"您可以在退出时保存或删除您的活动记录"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"请立即重置以删除会话活动记录;或者,您也可以在退出时保存或删除活动记录"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"选择照片"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 282d05b595b8..b94c6d16221c 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"慢速充電中"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"無線充電中"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"充電插座"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"非充電中"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"已連接,非充電中"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"已充滿電"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重設"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string> <string name="guest_resetting" msgid="7822120170191509566">"正在重設訪客…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"要重設訪客工作階段嗎?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"此操作會開始新的訪客工作階段,並刪除目前工作階段的所有應用程式和資料"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"要結束訪客模式嗎?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"此操作會刪除目前訪客工作階段中的所有應用程式和資料"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"結束"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"要儲存訪客活動嗎?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"您可儲存目前工作階段中的活動或刪除所有應用程式和資料"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"刪除"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"儲存"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"結束訪客模式"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"重設訪客工作階段"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"結束訪客模式"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"結束時將會刪除所有活動"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"您可以在結束時儲存或刪除活動"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"重設可立即刪除工作階段活動,或者您可以在結束時儲存或刪除活動"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"揀相"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index e439ef5d6edf..bb126c27d10a 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"慢速充電中"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"正在進行無線充電"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"充電座架"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"非充電中"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"已連接,尚未充電"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"充電完成"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"重設"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"移除"</string> <string name="guest_resetting" msgid="7822120170191509566">"正在重設訪客…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"要重設訪客工作階段嗎?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"這麼做將開始新的訪客工作階段,並刪除目前工作階段中的所有應用程式和資料"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"要結束訪客模式嗎?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"這麼做將刪除目前訪客工作階段中的所有應用程式和資料"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"結束"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"要儲存訪客活動嗎?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"你可以儲存目前工作階段中的活動,也可以刪除所有應用程式和資料"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"刪除"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"儲存"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"結束訪客模式"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"重設訪客工作階段"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"結束訪客模式"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"結束時將刪除所有活動"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"你可以在結束時儲存或刪除活動"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"重設即可立即刪除工作階段活動,你也可以在結束時儲存或刪除活動"</string> <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"選取相片"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index ebc9cb7927ff..c9d4b630a7e8 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -482,6 +482,7 @@ <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ishaja ngokushesha"</string> <string name="battery_info_status_charging_slow" msgid="3190803837168962319">"Ishaja kancane"</string> <string name="battery_info_status_charging_wireless" msgid="8924722966861282197">"Iyashaja ngaphandle kwentambo"</string> + <string name="battery_info_status_charging_dock" msgid="3554147903321236585">"Idokhu yokushaja"</string> <string name="battery_info_status_discharging" msgid="6962689305413556485">"Ayishaji"</string> <string name="battery_info_status_not_charging" msgid="3371084153747234837">"Ixhunyiwe, ayishaji"</string> <string name="battery_info_status_full" msgid="1339002294876531312">"Kushajiwe"</string> @@ -598,6 +599,21 @@ <string name="guest_reset_guest_confirm_button" msgid="2989915693215617237">"Setha kabusha"</string> <string name="guest_remove_guest_confirm_button" msgid="7858123434954143879">"Susa"</string> <string name="guest_resetting" msgid="7822120170191509566">"Ukusetha kabusha isimenywa…"</string> + <string name="guest_reset_and_restart_dialog_title" msgid="3396657008451616041">"Sesha kabusha isikhathi sesihambeli?"</string> + <string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Lokhu kuzoqala isikhathi sesihambeli esisha futhi kusule wonke ama-app nedatha kusuka esikhathini samanje"</string> + <string name="guest_exit_dialog_title" msgid="1846494656849381804">"Phuma kumodi yesihambeli?"</string> + <string name="guest_exit_dialog_message" msgid="1743218864242719783">"Lokhu kuzosula ama-app nedatha kusuka esikhathini sesihambeli samanje"</string> + <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Phuma"</string> + <string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Londoloza umsebenzi wesihambeli?"</string> + <string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Ungalondoloza umsebenzi kusuka esikhathini samanje noma usule wonke ama-app nedatha"</string> + <string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Sula"</string> + <string name="guest_exit_save_data_button" msgid="3690974510644963547">"Londoloza"</string> + <string name="guest_exit_button" msgid="5774985819191803960">"Phuma kumodi yesivakashi"</string> + <string name="guest_reset_button" msgid="2515069346223503479">"Setha kabusha isikhathi sesihambeli"</string> + <string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Phuma kusivakashi"</string> + <string name="guest_notification_ephemeral" msgid="7263252466950923871">"Wonke umsebenzi uzosulwa lapho uphuma"</string> + <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Ungalondoloza noma usule umsebenzi wakho lapho uphuma"</string> + <string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Setha kabusha ukuze usule umsebenzi wesikhathi manje, noma ungalondoloza noma usule umsebenzi lapho uphuma"</string> <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string> <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string> <string name="user_image_photo_selector" msgid="433658323306627093">"Khetha isithombe"</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 847f1dc541e8..f92cc8eda268 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1134,6 +1134,8 @@ <string name="battery_info_status_charging_slow">Charging slowly</string> <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when charging wirelessly. --> <string name="battery_info_status_charging_wireless">Charging wirelessly</string> + <!-- [CHAR_LIMIT=20] Battery use screen. Battery status shown in chart label when the device is dock charging. --> + <string name="battery_info_status_charging_dock">Charging Dock</string> <!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed --> <string name="battery_info_status_discharging">Not charging</string> <!-- Battery Info screen. Value for a status item. A state which device is connected with any charger(e.g. USB, Adapter or Wireless) but not charging yet. Used for diagnostic info screens, precise translation isn't needed --> @@ -1438,6 +1440,44 @@ <string name="guest_remove_guest_confirm_button">Remove</string> <!-- Status message indicating the device is in the process of resetting the guest user. [CHAR_LIMIT=NONE] --> <string name="guest_resetting">Resetting guest\u2026</string> + <!-- Dialog title on action reset and restart guest [CHAR LIMIT=60] --> + <string name="guest_reset_and_restart_dialog_title">Reset guest session?</string> + <!-- Dialog message on action reset and restart guest [CHAR LIMIT=160] --> + <string name="guest_reset_and_restart_dialog_message">This will start a new guest + session and delete all apps and data from the current session</string> + <!-- Dialog title on action exit guest (ephemeral guest) [CHAR LIMIT=32] --> + <string name="guest_exit_dialog_title">Exit guest mode?</string> + <!-- Dialog message on action exit guest (ephemeral guest) [CHAR LIMIT=80] --> + <string name="guest_exit_dialog_message">This will delete + apps and data from the current guest session</string> + <!-- Dialog button on action exit guest (ephemeral guest) [CHAR LIMIT=80] --> + <string name="guest_exit_dialog_button">Exit</string> + <!-- Dialog title on action exit guest (non-ephemeral guest) [CHAR LIMIT=32] --> + <string name="guest_exit_dialog_title_non_ephemeral">Save guest activity?</string> + <!-- Dialog message on action exit guest (non-ephemeral guest) [CHAR LIMIT=80] --> + <string name="guest_exit_dialog_message_non_ephemeral">You can save activity from + the current session or delete all apps and data</string> + <!-- Button on guest exit, clear data (non-ephemeral guest) [CHAR LIMIT=80] --> + <string name="guest_exit_clear_data_button">Delete</string> + <!-- Button on guest exit, save data (non-ephemeral guest) [CHAR LIMIT=80] --> + <string name="guest_exit_save_data_button">Save</string> + <!-- Label for button in confirmation dialog when exiting guest user [CHAR LIMIT=35] --> + <string name="guest_exit_button">Exit guest mode</string> + <!-- Label for button in confirmation dialog when resetting guest user [CHAR LIMIT=35] --> + <string name="guest_reset_button">Reset guest session</string> + <!-- Label for guest icon in quick settings user switcher [CHAR LIMIT=35] --> + <string name="guest_exit_quick_settings_button">Exit guest</string> + <!-- Message of the notification when guest mode is entered + and it's a ephemeral guest [CHAR LIMIT=60] --> + <string name="guest_notification_ephemeral">All activity will be deleted on exit</string> + <!-- Message of the notification when guest mode is entered + and it's not a ephemeral guest and it's a first time guest login [CHAR LIMIT=60] --> + <string name="guest_notification_non_ephemeral">You can save or delete your activity on exit</string> + <!-- Message of the notification when guest mode is entered + and it's not a ephemeral guest and it's not a first time guest login [CHAR LIMIT=NONE] --> + <string name="guest_notification_non_ephemeral_non_first_login">Reset to delete session + activity now, or you can save or delete activity on exit</string> + <!-- An option in a photo selection dialog to take a new photo [CHAR LIMIT=50] --> <string name="user_image_take_photo">Take a photo</string> <!-- An option in a photo selection dialog to choose a pre-existing image [CHAR LIMIT=50] --> diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index feb4212035bc..b9c4030d9d0e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -239,6 +239,8 @@ public class Utils { statusString = res.getString(R.string.battery_info_status_charging); break; } + } else if (batteryStatus.isPluggedInDock()) { + statusString = res.getString(R.string.battery_info_status_charging_dock); } else { statusString = res.getString(R.string.battery_info_status_charging_wireless); } diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp index 64563be78fbb..d463170fae8c 100644 --- a/packages/SettingsLib/tests/integ/Android.bp +++ b/packages/SettingsLib/tests/integ/Android.bp @@ -25,7 +25,7 @@ android_test { name: "SettingsLibTests", defaults: [ "SettingsLibDefaults", - "framework-wifi-test-defaults" + "framework-wifi-test-defaults", ], certificate: "platform", @@ -47,6 +47,7 @@ android_test { "androidx.test.espresso.core", "mockito-target-minus-junit4", "truth-prebuilt", + "SettingsLibDeviceStateRotationLock", "SettingsLibSettingsSpinner", "SettingsLibUsageProgressBarPreference", ], diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java index 09b2a2e73c5b..336cdd3f259f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java @@ -365,6 +365,17 @@ public class UtilsTest { } @Test + public void getBatteryStatus_chargingDock_returnDockChargingString() { + final Intent intent = new Intent(); + intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING); + intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_DOCK); + final Resources resources = mContext.getResources(); + + assertThat(Utils.getBatteryStatus(mContext, intent, /* compactStatus= */ false)).isEqualTo( + resources.getString(R.string.battery_info_status_charging_dock)); + } + + @Test public void getBatteryStatus_chargingWireless_returnWirelessChargingString() { final Intent intent = new Intent(); intent.putExtra(BatteryManager.EXTRA_STATUS, BatteryManager.BATTERY_STATUS_CHARGING); diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index f490c87aa4ec..4a104276c18f 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -32,6 +32,7 @@ android_app { ], static_libs: [ "junit", + "SettingsLibDeviceStateRotationLock", "SettingsLibDisplayDensityUtils", ], platform_apis: true, @@ -55,6 +56,7 @@ android_test { static_libs: [ "androidx.test.rules", "mockito-target-minus-junit4", + "SettingsLibDeviceStateRotationLock", "SettingsLibDisplayDensityUtils", "platform-test-annotations", "truth-prebuilt", diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java index e425790110aa..3cb143966a7c 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/DeviceSpecificSettings.java @@ -32,5 +32,6 @@ public class DeviceSpecificSettings { */ public static final String[] DEVICE_SPECIFIC_SETTINGS_TO_BACKUP = { Settings.Secure.DISPLAY_DENSITY_FORCED, + Settings.Secure.DEVICE_STATE_ROTATION_LOCK }; } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index 440bb67dc788..808ea9ede9dc 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -41,6 +41,7 @@ import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.LocalePicker; +import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManager; import java.util.ArrayList; import java.util.HashMap; @@ -200,6 +201,9 @@ public class SettingsHelper { } else if (Settings.Global.POWER_BUTTON_LONG_PRESS.equals(name)) { setLongPressPowerBehavior(cr, value); return; + } else if (Settings.System.ACCELEROMETER_ROTATION.equals(name) + && shouldSkipAutoRotateRestore()) { + return; } // Default case: write the restored value to settings @@ -236,6 +240,12 @@ public class SettingsHelper { } } + private boolean shouldSkipAutoRotateRestore() { + // When device state based auto rotation settings are available, let's skip the restoring + // of the standard auto rotation settings to avoid conflicting setting values. + return DeviceStateRotationLockSettingsManager.isDeviceStateRotationLockEnabled(mContext); + } + public String onBackupValue(String name, String value) { // Special processing for backing up ringtones & notification sounds if (Settings.System.RINGTONE.equals(name) diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index cce515444c1f..4f59ab4a03b4 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -422,6 +422,7 @@ public class SettingsBackupTest { Settings.Global.RADIO_NFC, Settings.Global.RADIO_WIFI, Settings.Global.RADIO_WIMAX, + Settings.Global.REMOVE_GUEST_ON_EXIT, Settings.Global.RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS, Settings.Global.READ_EXTERNAL_STORAGE_ENFORCED_DEFAULT, Settings.Global.RESTRICTED_NETWORKING_MODE, diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java index 4f7b494c0e71..ee76dbf8ce70 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java @@ -73,6 +73,8 @@ public class SettingsHelperTest { when(mContext.getSystemService(eq(Context.TELEPHONY_SERVICE))).thenReturn( mTelephonyManager); when(mContext.getResources()).thenReturn(mResources); + when(mContext.getApplicationContext()).thenReturn(mContext); + when(mContext.getContentResolver()).thenReturn(getContentResolver()); mSettingsHelper = spy(new SettingsHelper(mContext)); } @@ -305,6 +307,63 @@ public class SettingsHelperTest { new String[] { "he-IL", "id-ID", "yi" })); // supported } + @Test + public void restoreValue_autoRotation_deviceStateAutoRotationDisabled_restoresValue() { + when(mResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)) + .thenReturn(new String[]{}); + int previousValue = 0; + int newValue = 1; + setAutoRotationSettingValue(previousValue); + + restoreAutoRotationSetting(newValue); + + assertThat(getAutoRotationSettingValue()).isEqualTo(newValue); + } + + @Test + public void restoreValue_autoRotation_deviceStateAutoRotationEnabled_doesNotRestoreValue() { + when(mResources.getStringArray(R.array.config_perDeviceStateRotationLockDefaults)) + .thenReturn(new String[]{"0:1", "1:1"}); + int previousValue = 0; + int newValue = 1; + setAutoRotationSettingValue(previousValue); + + restoreAutoRotationSetting(newValue); + + assertThat(getAutoRotationSettingValue()).isEqualTo(previousValue); + } + + private int getAutoRotationSettingValue() { + return Settings.System.getInt( + getContentResolver(), + Settings.System.ACCELEROMETER_ROTATION, + /* default= */ -1); + } + + private void setAutoRotationSettingValue(int value) { + Settings.System.putInt( + getContentResolver(), + Settings.System.ACCELEROMETER_ROTATION, + value + ); + } + + private void restoreAutoRotationSetting(int newValue) { + mSettingsHelper.restoreValue( + mContext, + getContentResolver(), + new ContentValues(), + /* destination= */ Settings.System.CONTENT_URI, + /* name= */ Settings.System.ACCELEROMETER_ROTATION, + /* value= */ String.valueOf(newValue), + /* restoredFromSdkInt= */ 0); + } + + private ContentResolver getContentResolver() { + return InstrumentationRegistry.getInstrumentation().getTargetContext() + .getContentResolver(); + } + private void clearLongPressPowerValues() { ContentResolver cr = InstrumentationRegistry.getInstrumentation().getTargetContext() .getContentResolver(); diff --git a/packages/SystemUI/docs/device-entry/doze.md b/packages/SystemUI/docs/device-entry/doze.md index 5ff8851b4c69..6b6dce5da169 100644 --- a/packages/SystemUI/docs/device-entry/doze.md +++ b/packages/SystemUI/docs/device-entry/doze.md @@ -17,6 +17,9 @@ Note: The default UI used in AOD shares views with the Lock Screen and does not ### DOZE Device is asleep and listening for enabled pulsing and wake-up gesture triggers. In this state, no UI shows. +### DOZE_SUSPEND_TRIGGERS +Device is asleep and not listening for any triggers to wake up. This state is used only when CAR_MODE is active. In this state, no UI shows. + ### DOZE_AOD Device is asleep, showing UI, and listening for enabled pulsing and wake-up triggers. In this state, screen brightness is handled by [DozeScreenBrightness][5] which uses the brightness sensor specified by `doze_brightness_sensor_type` in the [SystemUI config][6]. To save power, this should be a low-powered sensor that shouldn't trigger as often as the light sensor used for on-screen adaptive brightness. diff --git a/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_off.xml new file mode 100644 index 000000000000..538f3284a301 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_off.xml @@ -0,0 +1,725 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> + +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="_R_G_L_2_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="7.062" + android:valueTo="8.578" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="221" + android:propertyName="translateX" + android:startOffset="28" + android:valueFrom="8.578" + android:valueTo="-1.789" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="translateX" + android:startOffset="248" + android:valueFrom="-1.789" + android:valueTo="-7" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="translateY" + android:startOffset="0" + android:valueFrom="3.312" + android:valueTo="2.016" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="221" + android:propertyName="translateY" + android:startOffset="28" + android:valueFrom="2.016" + android:valueTo="-8.789" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="translateY" + android:startOffset="248" + android:valueFrom="-8.789" + android:valueTo="-3.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="180" + android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="166" + android:propertyName="rotation" + android:startOffset="28" + android:valueFrom="90" + android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="55" + android:propertyName="rotation" + android:startOffset="193" + android:valueFrom="90" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_N_1_T_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="-135" + android:valueTo="-180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="-7" + android:valueTo="-8.859" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="221" + android:propertyName="translateX" + android:startOffset="28" + android:valueFrom="-8.859" + android:valueTo="1.69" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="translateX" + android:startOffset="248" + android:valueFrom="1.69" + android:valueTo="7" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="translateY" + android:startOffset="0" + android:valueFrom="-3.594" + android:valueTo="-1.922" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="221" + android:propertyName="translateY" + android:startOffset="28" + android:valueFrom="-1.922" + android:valueTo="8.627" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="translateY" + android:startOffset="248" + android:valueFrom="8.627" + android:valueTo="3.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="360" + android:valueTo="270" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="166" + android:propertyName="rotation" + android:startOffset="28" + android:valueFrom="270" + android:valueTo="270" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="55" + android:propertyName="rotation" + android:startOffset="193" + android:valueFrom="270" + android:valueTo="180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="rotation" + android:startOffset="248" + android:valueFrom="180" + android:valueTo="180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_N_1_T_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="-135" + android:valueTo="-180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="66" + android:propertyName="pathData" + android:startOffset="28" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="72" + android:propertyName="pathData" + android:startOffset="94" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="166" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="55" + android:propertyName="pathData" + android:startOffset="193" + android:valueFrom="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:startOffset="248" + android:valueFrom="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="290" + android:valueFrom="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c " + android:valueTo="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="83" + android:propertyName="pathData" + android:startOffset="317" + android:valueFrom="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c " + android:valueTo="M5.94 -2.52 C5.94,-2.52 5.94,-2.51 5.94,-2.51 C5.94,-2.51 5.93,-2.52 5.93,-2.52 C5.93,-2.52 4.53,-1.12 4.53,-1.12 C4.53,-1.12 4.55,-1.11 4.55,-1.11 C4.55,-1.11 4.53,-1.13 4.53,-1.13 C4.53,-1.13 4.55,-1.09 4.55,-1.09 C4.55,-1.09 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 5.94,-2.52 5.94,-2.52 C5.94,-2.52 5.94,-2.52 5.94,-2.52c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_1_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="66" + android:propertyName="pathData" + android:startOffset="28" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="72" + android:propertyName="pathData" + android:startOffset="94" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="166" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="55" + android:propertyName="pathData" + android:startOffset="193" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:startOffset="248" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="290" + android:valueFrom="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c " + android:valueTo="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="83" + android:propertyName="pathData" + android:startOffset="317" + android:valueFrom="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c " + android:valueTo="M-5.94 2.52 C-5.94,2.52 -5.94,2.51 -5.94,2.51 C-5.94,2.51 -5.93,2.52 -5.93,2.52 C-5.93,2.52 -4.53,1.12 -4.53,1.12 C-4.53,1.12 -4.55,1.11 -4.55,1.11 C-4.55,1.11 -4.53,1.13 -4.53,1.13 C-4.53,1.13 -4.55,1.09 -4.55,1.09 C-4.55,1.09 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -5.94,2.52 -5.94,2.52 C-5.94,2.52 -5.94,2.52 -5.94,2.52c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_2_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="61" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueTo="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="61" + android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueTo="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="143" + android:propertyName="pathData" + android:startOffset="88" + android:valueFrom="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c " + android:valueTo="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="232" + android:valueFrom="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c " + android:valueTo="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="pathData" + android:startOffset="248" + android:valueFrom="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c " + android:valueTo="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -8.52,-3.53 -8.52,-3.53c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_3_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="61" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueTo="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="28" + android:propertyName="pathData" + android:startOffset="61" + android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueTo="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="143" + android:propertyName="pathData" + android:startOffset="88" + android:valueFrom="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c " + android:valueTo="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="232" + android:valueFrom="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c " + android:valueTo="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="418" + android:propertyName="pathData" + android:startOffset="248" + android:valueFrom="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c " + android:valueTo="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 8.52,3.53 8.52,3.53c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="-135" + android:valueTo="-180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="683" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> + <aapt:attr name="android:drawable"> + <vector + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_2_G_N_1_T_0" + android:rotation="-135" + android:translateX="12.008" + android:translateY="11.992"> + <group + android:name="_R_G_L_2_G_T_1" + android:rotation="180" + android:translateX="7.062" + android:translateY="3.312"> + <group + android:name="_R_G_L_2_G" + android:translateX="6.984" + android:translateY="3.547"> + <path + android:name="_R_G_L_2_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " /> + </group> + </group> + </group> + <group + android:name="_R_G_L_1_G_N_1_T_0" + android:rotation="-135" + android:translateX="12.008" + android:translateY="11.992"> + <group + android:name="_R_G_L_1_G_T_1" + android:rotation="360" + android:translateX="-7" + android:translateY="-3.594"> + <group + android:name="_R_G_L_1_G" + android:translateX="6.984" + android:translateY="3.547"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " /> + </group> + </group> + </group> + <group + android:name="_R_G_L_0_G" + android:rotation="-135" + android:translateX="12.008" + android:translateY="11.992"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c " /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " /> + <path + android:name="_R_G_L_0_G_D_3_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_on.xml new file mode 100644 index 000000000000..bd67d9f8dbaa --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/qs_auto_rotate_icon_on.xml @@ -0,0 +1,781 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> + +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="_R_G_L_2_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="fillAlpha" + android:startOffset="367" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:startOffset="17" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -6.08,2.36 -6.08,2.36 C-6.08,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 7.49,1.78 7.49,1.78 C7.49,1.78 8.87,0.41 8.87,0.41 C8.87,0.41 8.95,0.5 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="45" + android:propertyName="pathData" + android:startOffset="59" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -2.78,5.89 -2.78,5.89 C-2.78,5.89 -1.29,4.47 -1.29,4.47 C-1.29,4.47 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="103" + android:valueFrom="M8.82 3.18 C8.26,3.74 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 0.36,8.83 0.36,8.83 C0.36,8.83 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="34" + android:propertyName="pathData" + android:startOffset="121" + android:valueFrom="M8.82 3.18 C8.26,3.74 3.22,8.8 3.22,8.8 C3.22,8.8 3.21,8.79 3.21,8.79 C3.21,8.79 3.24,8.8 3.24,8.8 C3.24,8.8 1.8,7.44 1.8,7.44 C1.8,7.44 1.78,7.42 1.78,7.42 C1.78,7.42 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="26" + android:propertyName="pathData" + android:startOffset="155" + android:valueFrom="M8.82 3.18 C8.26,3.74 5.82,6.14 5.82,6.14 C5.82,6.14 5.81,6.14 5.81,6.14 C5.81,6.14 5.83,6.14 5.83,6.14 C5.83,6.14 4.39,4.78 4.39,4.78 C4.39,4.78 4.37,4.76 4.37,4.76 C4.37,4.76 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.7,2.29 8.82,3.18c " + android:valueTo="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="181" + android:valueFrom="M8.91 3.09 C8.91,3.09 8.91,3.1 8.91,3.1 C8.91,3.1 8.9,3.09 8.9,3.09 C8.9,3.09 7.49,1.74 7.49,1.74 C7.49,1.74 7.5,1.75 7.5,1.75 C7.5,1.75 7.48,1.73 7.48,1.73 C7.48,1.73 7.5,1.77 7.5,1.77 C7.5,1.77 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 8.36,-0.12 9.05,0.58 C9.86,1.4 9.79,2.2 8.91,3.09c " + android:valueTo="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="52" + android:propertyName="pathData" + android:startOffset="198" + android:valueFrom="M9.05 0.57 C9.05,0.57 9.05,0.58 9.05,0.58 C9.05,0.58 9.04,0.57 9.04,0.57 C9.04,0.57 7.6,1.83 7.6,1.83 C7.6,1.83 7.61,1.85 7.61,1.85 C7.61,1.85 7.59,1.83 7.59,1.83 C7.59,1.83 7.61,1.86 7.61,1.86 C7.61,1.86 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 9.05,0.58 9.05,0.58 C9.05,0.58 9.05,0.57 9.05,0.57c " + android:valueTo="M5.94 -2.52 C5.94,-2.52 5.94,-2.51 5.94,-2.51 C5.94,-2.51 5.93,-2.52 5.93,-2.52 C5.93,-2.52 4.53,-1.12 4.53,-1.12 C4.53,-1.12 4.55,-1.11 4.55,-1.11 C4.55,-1.11 4.53,-1.13 4.53,-1.13 C4.53,-1.13 4.55,-1.09 4.55,-1.09 C4.55,-1.09 4.54,-1.11 4.54,-1.11 C4.54,-1.11 5.95,-2.51 5.95,-2.51 C5.95,-2.51 5.94,-2.52 5.94,-2.52 C5.94,-2.52 5.94,-2.52 5.94,-2.52c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_1_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="fillAlpha" + android:startOffset="367" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_1_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="41" + android:propertyName="pathData" + android:startOffset="17" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 6.08,-2.36 6.08,-2.36 C6.08,-2.36 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -7.49,-1.78 -7.49,-1.78 C-7.49,-1.78 -8.87,-0.41 -8.87,-0.41 C-8.87,-0.41 -8.95,-0.5 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="45" + android:propertyName="pathData" + android:startOffset="59" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 2.78,-5.89 2.78,-5.89 C2.78,-5.89 1.29,-4.47 1.29,-4.47 C1.29,-4.47 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="103" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 -0.36,-8.83 -0.36,-8.83 C-0.36,-8.83 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="34" + android:propertyName="pathData" + android:startOffset="121" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -3.22,-8.8 -3.22,-8.8 C-3.22,-8.8 -3.21,-8.79 -3.21,-8.79 C-3.21,-8.79 -3.24,-8.8 -3.24,-8.8 C-3.24,-8.8 -1.8,-7.44 -1.8,-7.44 C-1.8,-7.44 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="26" + android:propertyName="pathData" + android:startOffset="155" + android:valueFrom="M-8.82 -3.18 C-8.26,-3.74 -5.81,-6.14 -5.81,-6.14 C-5.81,-6.14 -5.81,-6.13 -5.81,-6.13 C-5.81,-6.13 -5.83,-6.14 -5.83,-6.14 C-5.83,-6.14 -4.39,-4.78 -4.39,-4.78 C-4.39,-4.78 -4.37,-4.76 -4.37,-4.76 C-4.37,-4.76 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.7,-2.29 -8.82,-3.18c " + android:valueTo="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="181" + android:valueFrom="M-8.91 -3.09 C-8.91,-3.09 -8.91,-3.09 -8.91,-3.09 C-8.91,-3.09 -8.9,-3.09 -8.9,-3.09 C-8.9,-3.09 -7.49,-1.74 -7.49,-1.74 C-7.49,-1.74 -7.5,-1.75 -7.5,-1.75 C-7.5,-1.75 -7.48,-1.73 -7.48,-1.73 C-7.48,-1.73 -7.5,-1.77 -7.5,-1.77 C-7.5,-1.77 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -8.36,0.12 -9.05,-0.58 C-9.86,-1.4 -9.79,-2.2 -8.91,-3.09c " + android:valueTo="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="52" + android:propertyName="pathData" + android:startOffset="198" + android:valueFrom="M-9.05 -0.57 C-9.05,-0.57 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.04,-0.57 -9.04,-0.57 C-9.04,-0.57 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.85 -7.61,-1.85 C-7.61,-1.85 -7.59,-1.83 -7.59,-1.83 C-7.59,-1.83 -7.61,-1.86 -7.61,-1.86 C-7.61,-1.86 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -9.05,-0.58 -9.05,-0.58 C-9.05,-0.58 -9.05,-0.57 -9.05,-0.57c " + android:valueTo="M-5.94 2.52 C-5.94,2.52 -5.94,2.51 -5.94,2.51 C-5.94,2.51 -5.93,2.52 -5.93,2.52 C-5.93,2.52 -4.53,1.12 -4.53,1.12 C-4.53,1.12 -4.55,1.11 -4.55,1.11 C-4.55,1.11 -4.53,1.13 -4.53,1.13 C-4.53,1.13 -4.55,1.09 -4.55,1.09 C-4.55,1.09 -4.54,1.11 -4.54,1.11 C-4.54,1.11 -5.95,2.51 -5.95,2.51 C-5.95,2.51 -5.94,2.52 -5.94,2.52 C-5.94,2.52 -5.94,2.52 -5.94,2.52c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_2_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="38" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueTo="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="38" + android:valueFrom="M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " + android:valueTo="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="90" + android:propertyName="pathData" + android:startOffset="55" + android:valueFrom="M6.15 -2.35 C6.15,-2.35 6.15,-2.35 6.15,-2.35 C6.16,-2.34 6.15,-2.35 6.16,-2.35 C6.16,-2.35 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 4.72,-0.93 4.72,-0.93 C4.72,-0.93 4.7,-0.94 4.7,-0.94 C4.7,-0.94 6.15,-2.35 6.15,-2.35c " + android:valueTo="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="10" + android:propertyName="pathData" + android:startOffset="145" + android:valueFrom="M-0.65 -9.12 C-0.65,-9.12 -0.66,-9.13 -0.66,-9.13 C-0.65,-9.12 -0.66,-9.12 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -0.65,-9.12 -0.65,-9.12c " + android:valueTo="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="pathData" + android:startOffset="155" + android:valueFrom="M-3.21 -8.85 C-3.21,-8.85 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -1.8,-7.43 -1.8,-7.43 C-1.8,-7.43 -3.21,-8.85 -3.21,-8.85c " + android:valueTo="M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.94 7.53,-0.94 C7.53,-0.94 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -8.52,-3.53 -8.52,-3.53c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G_D_3_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="38" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueTo="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="17" + android:propertyName="pathData" + android:startOffset="38" + android:valueFrom="M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " + android:valueTo="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="90" + android:propertyName="pathData" + android:startOffset="55" + android:valueFrom="M-6.15 2.35 C-6.15,2.35 -6.15,2.35 -6.15,2.35 C-6.16,2.34 -6.15,2.35 -6.15,2.35 C-6.15,2.35 -6.14,2.36 -6.14,2.36 C-6.14,2.36 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -4.72,0.93 -4.72,0.93 C-4.72,0.93 -4.7,0.94 -4.7,0.94 C-4.7,0.94 -6.15,2.35 -6.15,2.35c " + android:valueTo="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="10" + android:propertyName="pathData" + android:startOffset="145" + android:valueFrom="M0.65 9.12 C0.65,9.12 0.66,9.13 0.66,9.13 C0.65,9.12 0.66,9.13 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 0.65,9.12 0.65,9.12c " + android:valueTo="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="pathData" + android:startOffset="155" + android:valueFrom="M3.21 8.85 C3.21,8.85 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 1.8,7.43 1.8,7.43 C1.8,7.43 3.21,8.85 3.21,8.85c " + android:valueTo="M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 8.52,3.53 8.52,3.53c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_2_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="-135" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="-7" + android:valueTo="-8.859" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="138" + android:propertyName="translateX" + android:startOffset="17" + android:valueFrom="-8.859" + android:valueTo="1.734" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="translateX" + android:startOffset="155" + android:valueFrom="1.734" + android:valueTo="7" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="translateY" + android:startOffset="0" + android:valueFrom="-3.594" + android:valueTo="-1.922" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="138" + android:propertyName="translateY" + android:startOffset="17" + android:valueFrom="-1.922" + android:valueTo="8.671" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="translateY" + android:startOffset="155" + android:valueFrom="8.671" + android:valueTo="3.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="360" + android:valueTo="270" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="103" + android:propertyName="rotation" + android:startOffset="17" + android:valueFrom="270" + android:valueTo="270" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="34" + android:propertyName="rotation" + android:startOffset="121" + android:valueFrom="270" + android:valueTo="180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="rotation" + android:startOffset="155" + android:valueFrom="180" + android:valueTo="180" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_N_3_T_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="-135" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="7.062" + android:valueTo="8.578" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="138" + android:propertyName="translateX" + android:startOffset="17" + android:valueFrom="8.578" + android:valueTo="-1.656" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="translateX" + android:startOffset="155" + android:valueFrom="-1.656" + android:valueTo="-7" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="translateY" + android:startOffset="0" + android:valueFrom="3.312" + android:valueTo="2.016" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="138" + android:propertyName="translateY" + android:startOffset="17" + android:valueFrom="2.016" + android:valueTo="-8.656" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="261" + android:propertyName="translateY" + android:startOffset="155" + android:valueFrom="-8.656" + android:valueTo="-3.5" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.1,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="17" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="180" + android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="103" + android:propertyName="rotation" + android:startOffset="17" + android:valueFrom="90" + android:valueTo="90" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="34" + android:propertyName="rotation" + android:startOffset="121" + android:valueFrom="90" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_N_3_T_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="367" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="-135" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="433" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> + <aapt:attr name="android:drawable"> + <vector + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_2_G" + android:rotation="0" + android:translateX="12" + android:translateY="12"> + <path + android:name="_R_G_L_2_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M8.52 3.53 C8.52,3.53 2.94,9.08 2.94,9.08 C2.3,9.72 1.27,9.74 0.65,9.12 C0.65,9.12 -7.53,0.95 -7.53,0.95 C-7.53,0.95 -4.7,0.94 -4.7,0.94 C-4.7,0.94 1.78,7.42 1.78,7.42 C1.78,7.42 7.11,2.11 7.11,2.11 C7.11,2.11 7.14,2.14 7.14,2.14 C7.14,2.14 8.48,3.49 8.48,3.49 C8.48,3.49 8.5,3.51 8.5,3.51 C8.5,3.51 8.52,3.53 8.52,3.53c " /> + <path + android:name="_R_G_L_2_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-8.52 -3.53 C-8.52,-3.53 -2.94,-9.08 -2.94,-9.08 C-2.3,-9.72 -1.27,-9.74 -0.65,-9.12 C-0.65,-9.12 7.53,-0.95 7.53,-0.95 C7.53,-0.95 4.7,-0.94 4.7,-0.94 C4.7,-0.94 -1.78,-7.42 -1.78,-7.42 C-1.78,-7.42 -7.11,-2.11 -7.11,-2.11 C-7.11,-2.11 -7.14,-2.14 -7.14,-2.14 C-7.14,-2.14 -8.48,-3.49 -8.48,-3.49 C-8.48,-3.49 -8.5,-3.51 -8.5,-3.51 C-8.5,-3.51 -8.52,-3.53 -8.52,-3.53c " /> + <path + android:name="_R_G_L_2_G_D_2_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M7.54 -0.94 C7.54,-0.94 7.54,-0.95 7.54,-0.95 C7.55,-0.94 7.54,-0.94 7.55,-0.94 C7.55,-0.94 7.53,-0.94 7.53,-0.94 C7.53,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94 C7.54,-0.94 7.52,-0.94 7.52,-0.94 C7.52,-0.94 7.54,-0.94 7.54,-0.94c " /> + <path + android:name="_R_G_L_2_G_D_3_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-7.54 0.94 C-7.54,0.94 -7.54,0.95 -7.54,0.95 C-7.55,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.53,0.94 -7.53,0.94 C-7.53,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94 C-7.54,0.94 -7.52,0.94 -7.52,0.94 C-7.52,0.94 -7.54,0.94 -7.54,0.94c " /> + </group> + <group + android:name="_R_G_L_1_G_N_3_T_0" + android:rotation="0" + android:translateX="12" + android:translateY="12"> + <group + android:name="_R_G_L_1_G_T_1" + android:rotation="360" + android:translateX="-7" + android:translateY="-3.594"> + <group + android:name="_R_G_L_1_G" + android:translateX="6.984" + android:translateY="3.547"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " /> + </group> + </group> + </group> + <group + android:name="_R_G_L_0_G_N_3_T_0" + android:rotation="0" + android:translateX="12" + android:translateY="12"> + <group + android:name="_R_G_L_0_G_T_1" + android:rotation="180" + android:translateX="7.062" + android:translateY="3.312"> + <group + android:name="_R_G_L_0_G" + android:translateX="6.984" + android:translateY="3.547"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-4.6 -1.06 C-4.6,-1.06 -9.55,-1.06 -9.55,-1.06 C-9.55,-1.06 -9.55,-6.01 -9.55,-6.01 C-9.55,-6.01 -7.55,-6 -7.55,-6 C-7.55,-6 -7.51,-3.04 -7.51,-3.04 C-7.51,-3.04 -4.6,-3.05 -4.6,-3.05 C-4.6,-3.05 -4.6,-1.06 -4.6,-1.06c " /> + </group> + </group> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_off.xml new file mode 100644 index 000000000000..17a76116db25 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_off.xml @@ -0,0 +1,144 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 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. + --> + +<animated-vector + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="android:drawable"> + <vector + android:height="24dp" + android:width="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group android:name="_R_G_L_2_G" + android:translateX="14.125" + android:translateY="12"> + <path + android:name="_R_G_L_2_G_D_0_P_0" + android:fillColor="#ffffff" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M-1 6.17 C-1,6.17 0.88,4.29 0.88,4.29 C0.88,4.29 -1,2.41 -1,2.41 C-1,2.41 -1,6.17 -1,6.17c M0.88 -4.29 C0.88,-4.29 -1,-6.17 -1,-6.17 C-1,-6.17 -1,-2.41 -1,-2.41 C-1,-2.41 0.88,-4.29 0.88,-4.29c M-2 -10 C-2,-10 3.71,-4.29 3.71,-4.29 C3.71,-4.29 -0.59,0 -0.59,0 C-0.59,0 3.71,4.29 3.71,4.29 C3.71,4.29 -2,10 -2,10 C-2,10 -3,10 -3,10 C-3,10 -3,2.41 -3,2.41 C-3,2.41 -7.59,7 -7.59,7 C-7.59,7 -9.01,5.59 -9.01,5.59 C-9.01,5.59 -3.41,0 -3.41,0 C-3.41,0 -9.01,-5.59 -9.01,-5.59 C-9.01,-5.59 -7.59,-7 -7.59,-7 C-7.59,-7 -3,-2.41 -3,-2.41 C-3,-2.41 -3,-10 -3,-10 C-3,-10 -2,-10 -2,-10c "/> + </group> + <group + android:name="_R_G_L_1_G" + android:translateX="14.125" + android:translateY="12" + android:pivotX="-9.109" + android:scaleX="1" + android:scaleY="1"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillColor="#ffffff" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M-9.09 -1.5 C-8.26,-1.5 -7.59,-0.83 -7.59,0 C-7.59,0.83 -8.26,1.5 -9.09,1.5 C-9.91,1.5 -10.59,0.83 -10.59,0 C-10.59,-0.83 -9.91,-1.5 -9.09,-1.5c "/> + </group> + <group + android:name="_R_G_L_0_G" + android:translateX="14.125" + android:translateY="12" + android:pivotX="4.875" + android:scaleX="1" + android:scaleY="1"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillColor="#ffffff" + android:fillAlpha="1" + android:fillType="nonZero" + android:pathData=" M4.92 -1.5 C5.75,-1.5 6.42,-0.83 6.42,0 C6.42,0.83 5.75,1.5 4.92,1.5 C4.09,1.5 3.42,0.83 3.42,0 C3.42,-0.83 4.09,-1.5 4.92,-1.5c "/> + </group> + </group> + <group android:name="time_group"/> + </vector> + </aapt:attr> + <target android:name="_R_G_L_1_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:propertyName="scaleX" + android:duration="150" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:propertyName="scaleY" + android:duration="150" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr + name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:propertyName="scaleX" + android:duration="150" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:propertyName="scaleY" + android:duration="150" + android:startOffset="0" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator + android:pathData="M 0.0,0.0 c0.5,0 0.833,0.833 1.0,1.0"/> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:propertyName="translateX" + android:duration="150" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"/> + </set> + </aapt:attr> + </target> +</animated-vector> diff --git a/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_on.xml new file mode 100644 index 000000000000..2dba48cf155d --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_on.xml @@ -0,0 +1,138 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> + +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="_R_G_L_1_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="scaleX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="250" + android:propertyName="scaleY" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="scaleX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="250" + android:propertyName="scaleY" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.35,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="267" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> + <aapt:attr name="android:drawable"> + <vector + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_2_G" + android:translateX="14.125" + android:translateY="12"> + <path + android:name="_R_G_L_2_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-1 6.17 C-1,6.17 0.88,4.29 0.88,4.29 C0.88,4.29 -1,2.41 -1,2.41 C-1,2.41 -1,6.17 -1,6.17c M0.88 -4.29 C0.88,-4.29 -1,-6.17 -1,-6.17 C-1,-6.17 -1,-2.41 -1,-2.41 C-1,-2.41 0.88,-4.29 0.88,-4.29c M-2 -10 C-2,-10 3.71,-4.29 3.71,-4.29 C3.71,-4.29 -0.59,0 -0.59,0 C-0.59,0 3.71,4.29 3.71,4.29 C3.71,4.29 -2,10 -2,10 C-2,10 -3,10 -3,10 C-3,10 -3,2.41 -3,2.41 C-3,2.41 -7.59,7 -7.59,7 C-7.59,7 -9.01,5.59 -9.01,5.59 C-9.01,5.59 -3.41,0 -3.41,0 C-3.41,0 -9.01,-5.59 -9.01,-5.59 C-9.01,-5.59 -7.59,-7 -7.59,-7 C-7.59,-7 -3,-2.41 -3,-2.41 C-3,-2.41 -3,-10 -3,-10 C-3,-10 -2,-10 -2,-10c " /> + </group> + <group + android:name="_R_G_L_1_G" + android:pivotX="-9.109" + android:scaleX="0" + android:scaleY="0" + android:translateX="14.125" + android:translateY="12"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-9.09 -1.5 C-8.26,-1.5 -7.59,-0.83 -7.59,0 C-7.59,0.83 -8.26,1.5 -9.09,1.5 C-9.91,1.5 -10.59,0.83 -10.59,0 C-10.59,-0.83 -9.91,-1.5 -9.09,-1.5c " /> + </group> + <group + android:name="_R_G_L_0_G" + android:pivotX="4.875" + android:scaleX="0" + android:scaleY="0" + android:translateX="14.125" + android:translateY="12"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M4.92 -1.5 C5.75,-1.5 6.42,-0.83 6.42,0 C6.42,0.83 5.75,1.5 4.92,1.5 C4.09,1.5 3.42,0.83 3.42,0 C3.42,-0.83 4.09,-1.5 4.92,-1.5c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_search.xml b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_search.xml new file mode 100644 index 000000000000..3697769cfa34 --- /dev/null +++ b/packages/SystemUI/res-keyguard/drawable/qs_bluetooth_icon_search.xml @@ -0,0 +1,268 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (C) 2020 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. + --> + +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="_R_G_L_0_G_D_1_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="167" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="333" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_2_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="167" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="333" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="500" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_3_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="167" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="333" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_0_G_D_4_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="167" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="333" + android:valueFrom="1" + android:valueTo="1" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="167" + android:propertyName="fillAlpha" + android:startOffset="500" + android:valueFrom="1" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="1017" + android:propertyName="translateX" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="1" + android:valueType="floatType" /> + </set> + </aapt:attr> + </target> + <aapt:attr name="android:drawable"> + <vector + android:width="24dp" + android:height="24dp" + android:viewportHeight="24" + android:viewportWidth="24"> + <group android:name="_R_G"> + <group + android:name="_R_G_L_0_G" + android:translateX="14.125" + android:translateY="12"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-1 6.17 C-1,6.17 0.88,4.29 0.88,4.29 C0.88,4.29 -1,2.41 -1,2.41 C-1,2.41 -1,6.17 -1,6.17c M0.88 -4.29 C0.88,-4.29 -1,-6.17 -1,-6.17 C-1,-6.17 -1,-2.41 -1,-2.41 C-1,-2.41 0.88,-4.29 0.88,-4.29c M-2 -10 C-2,-10 3.71,-4.29 3.71,-4.29 C3.71,-4.29 -0.59,0 -0.59,0 C-0.59,0 3.71,4.29 3.71,4.29 C3.71,4.29 -2,10 -2,10 C-2,10 -3,10 -3,10 C-3,10 -3,2.41 -3,2.41 C-3,2.41 -7.59,7 -7.59,7 C-7.59,7 -9.01,5.59 -9.01,5.59 C-9.01,5.59 -3.41,0 -3.41,0 C-3.41,0 -9.01,-5.59 -9.01,-5.59 C-9.01,-5.59 -7.59,-7 -7.59,-7 C-7.59,-7 -3,-2.41 -3,-2.41 C-3,-2.41 -3,-10 -3,-10 C-3,-10 -2,-10 -2,-10c " /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M4.56 2.33 C4.56,2.33 2.24,0.01 2.24,0.01 C2.24,0.01 4.57,-2.31 4.57,-2.31 C4.84,-1.59 5,-0.82 5,0 C5,0.82 4.84,1.61 4.56,2.33c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M6.27 -4.03 C6.27,-4.03 7.53,-5.29 7.53,-5.29 C8.46,-3.77 9,-1.99 9.01,-0.1 C9.01,1.85 8.44,3.67 7.47,5.21 C7.47,5.21 6.27,4.01 6.27,4.01 C6.89,2.81 7.25,1.44 7.25,-0.01 C7.25,-1.46 6.9,-2.82 6.27,-4.03c " /> + <path + android:name="_R_G_L_0_G_D_3_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-8.6 -2.33 C-8.6,-2.33 -6.28,-0.01 -6.28,-0.01 C-6.28,-0.01 -8.61,2.31 -8.61,2.31 C-8.88,1.59 -9.04,0.82 -9.04,0 C-9.04,-0.82 -8.88,-1.61 -8.6,-2.33c " /> + <path + android:name="_R_G_L_0_G_D_4_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-10.31 4.03 C-10.31,4.03 -11.57,5.29 -11.57,5.29 C-12.5,3.77 -13.04,1.99 -13.05,0.1 C-13.05,-1.85 -12.48,-3.67 -11.51,-5.21 C-11.51,-5.21 -10.31,-4.01 -10.31,-4.01 C-10.93,-2.81 -11.29,-1.44 -11.29,0.01 C-11.29,1.46 -10.94,2.82 -10.31,4.03c " /> + <path + android:name="_R_G_L_0_G_D_5_P_0" + android:fillAlpha="1" + android:fillColor="#000000" + android:fillType="nonZero" + android:pathData=" M-9.09 0 C-9.09,0 -9.09,0 -9.09,0 C-9.09,0 -9.09,0 -9.09,0 C-9.09,0 -9.09,0 -9.09,0 C-9.09,0 -9.09,0 -9.09,0c " /> + <path + android:name="_R_G_L_0_G_D_6_P_0" + android:fillAlpha="1" + android:fillColor="#000000" + android:fillType="nonZero" + android:pathData=" M4.92 0 C4.92,0 4.92,0 4.92,0 C4.92,0 4.92,0 4.92,0 C4.92,0 4.92,0 4.92,0 C4.92,0 4.92,0 4.92,0c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml index 64aa8ee84423..560cfe6f7a11 100644 --- a/packages/SystemUI/res-keyguard/values/strings.xml +++ b/packages/SystemUI/res-keyguard/values/strings.xml @@ -164,21 +164,21 @@ Displayed in a dialog box. --> <string name="kg_password_wrong_pin_code_pukked">Incorrect SIM PIN code you must now contact your carrier to unlock your device.</string> <!-- Instructions telling the user that they entered the wrong SIM PIN while trying - to unlock the keyguard. Displayed in a dialog box. --> - <plurals name="kg_password_wrong_pin_code"> - <item quantity="one">Incorrect SIM PIN code, you have <xliff:g id="number">%d</xliff:g> remaining attempt before you must contact your carrier to unlock your device.</item> - <item quantity="other">Incorrect SIM PIN code, you have <xliff:g id="number">%d</xliff:g> remaining attempts.</item> - </plurals> + to unlock the keyguard. Displayed in a dialog box. [CHAR LIMIT=NONE] --> + <string name="kg_password_wrong_pin_code"> {count, plural, + =1 {Incorrect SIM PIN code, you have # remaining attempt before you must contact your carrier to unlock your device.} + other {Incorrect SIM PIN code, you have # remaining attempts. } + }</string> <!-- Instructions telling the user that they have exhausted SIM PUK retries and the SIM is now unusable. Displayed in a dialog box. --> <string name="kg_password_wrong_puk_code_dead">SIM is unusable. Contact your carrier.</string> <!-- Instructions telling the user that they entered the wrong puk while trying - to unlock the keyguard. Displayed in a dialog box. --> - <plurals name="kg_password_wrong_puk_code"> - <item quantity="one">Incorrect SIM PUK code, you have <xliff:g id="number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable.</item> - <item quantity="other">Incorrect SIM PUK code, you have <xliff:g id="number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable.</item> - </plurals> + to unlock the keyguard. Displayed in a dialog box. [CHAR LIMIT=NONE] --> + <string name="kg_password_wrong_puk_code">{count, plural, + =1 {Incorrect SIM PUK code, you have # remaining attempt before SIM becomes permanently unusable.} + other {Incorrect SIM PUK code, you have # remaining attempts before SIM becomes permanently unusable.} + }</string> <!-- Instructions telling the user that the operation to unlock the keyguard with SIM PIN failed. Displayed in one line in a large font. --> <string name="kg_password_pin_failed">SIM PIN operation failed!</string> @@ -223,21 +223,17 @@ <!-- Error message indicating that the camera privacy sensor has been turned on [CHAR LIMIT=NONE] --> <string name="kg_face_sensor_privacy_enabled">To use Face Unlock, turn on <b>Camera access</b> in Settings > Privacy</string> - <!-- Instructions telling the user remaining times when enter SIM PIN view. --> - <plurals name="kg_password_default_pin_message"> - <item quantity="one">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining -attempt before you must contact your carrier to unlock your device.</item> - <item quantity="other">Enter SIM PIN. You have <xliff:g id="number">%d</xliff:g> remaining -attempts.</item> - </plurals> - - <!-- Instructions telling the user remaining times when enter SIM PUK view. --> - <plurals name="kg_password_default_puk_message"> - <item quantity="one">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id=" -number">%d</xliff:g> remaining attempt before SIM becomes permanently unusable. Contact carrier for details.</item> - <item quantity="other">SIM is now disabled. Enter PUK code to continue. You have <xliff:g id=" -number">%d</xliff:g> remaining attempts before SIM becomes permanently unusable. Contact carrier for details.</item> - </plurals> + <!-- Instructions telling the user remaining times when enter SIM PIN view. [CHAR LIMIT=NONE] --> + <string name="kg_password_default_pin_message">{count, plural, + =1 {Enter SIM PIN. You have # remaining attempt before you must contact your carrier to unlock your device.} + other {Enter SIM PIN. You have # remaining attempts.} + }</string> + + <!-- Instructions telling the user remaining times when enter SIM PUK view. [CHAR LIMIT=NONE] --> + <string name="kg_password_default_puk_message">{count, plural, + =1 {SIM is now disabled. Enter PUK code to continue. You have # remaining attempt before SIM becomes permanently unusable. Contact carrier for details.} + other {SIM is now disabled. Enter PUK code to continue. You have # remaining attempts before SIM becomes permanently unusable. Contact carrier for details.} + }</string> <!-- Name of the "Default" clock face, which is the clock face that will be shown by default. [CHAR LIMIT=15]--> <string name="clock_title_default">Default</string> diff --git a/packages/SystemUI/res/drawable/broadcast_dialog_btn_bg.xml b/packages/SystemUI/res/drawable/broadcast_dialog_btn_bg.xml new file mode 100644 index 000000000000..5fd7ee29d838 --- /dev/null +++ b/packages/SystemUI/res/drawable/broadcast_dialog_btn_bg.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> + +<shape xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:shape="rectangle"> + <solid android:color="?androidprv:attr/colorAccentPrimary" /> + <corners android:radius="@dimen/broadcast_dialog_btn_radius" /> +</shape> diff --git a/packages/SystemUI/res/drawable/ic_qs_screen_saver.xml b/packages/SystemUI/res/drawable/ic_qs_screen_saver.xml new file mode 100644 index 000000000000..263a3d1c894c --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_qs_screen_saver.xml @@ -0,0 +1,24 @@ +<!-- + Copyright (C) 2022 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. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?android:attr/colorControlNormal"> + <path android:fillColor="@android:color/white" + android:pathData="M5,15H19L14.5,9L11,13.5L8.5,10.5ZM6,21Q5.575,21 5.287,20.712Q5,20.425 5,20V19H3Q2.175,19 1.588,18.413Q1,17.825 1,17V6Q1,5.175 1.588,4.588Q2.175,4 3,4H21Q21.825,4 22.413,4.588Q23,5.175 23,6V17Q23,17.825 22.413,18.413Q21.825,19 21,19H19V20Q19,20.425 18.712,20.712Q18.425,21 18,21ZM3,17H21Q21,17 21,17Q21,17 21,17V6Q21,6 21,6Q21,6 21,6H3Q3,6 3,6Q3,6 3,6V17Q3,17 3,17Q3,17 3,17ZM3,17Q3,17 3,17Q3,17 3,17V6Q3,6 3,6Q3,6 3,6Q3,6 3,6Q3,6 3,6V17Q3,17 3,17Q3,17 3,17Z"/> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml index 3a08a7111d9a..41123c84ded1 100644 --- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml +++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml @@ -16,19 +16,13 @@ * limitations under the License. */ --> -<ripple +<shape xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:color="?android:attr/textColorPrimary"> - <item> - <shape - android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurface"/> - <size - android:width="@dimen/keyguard_affordance_width" - android:height="@dimen/keyguard_affordance_height"/> - <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/> - </shape> - </item> -</ripple> - + android:shape="rectangle"> + <solid android:color="?androidprv:attr/colorSurface"/> + <size + android:width="@dimen/keyguard_affordance_width" + android:height="@dimen/keyguard_affordance_height"/> + <corners android:radius="@dimen/keyguard_affordance_fixed_radius"/> +</shape> diff --git a/packages/SystemUI/res/drawable/settings_input_antenna.xml b/packages/SystemUI/res/drawable/settings_input_antenna.xml new file mode 100644 index 000000000000..f2adcaf069ef --- /dev/null +++ b/packages/SystemUI/res/drawable/settings_input_antenna.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 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. +--> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" + android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" + android:tint="?android:attr/textColorSecondary"> + <path android:fillColor="#FF000000" + android:pathData="M9,22.4 L7.6,21 11,17.6V14.3Q10.325,14 9.913,13.375Q9.5,12.75 9.5,12Q9.5,10.95 10.225,10.225Q10.95,9.5 12,9.5Q13.05,9.5 13.775,10.225Q14.5,10.95 14.5,12Q14.5,12.75 14.088,13.375Q13.675,14 13,14.3V17.6L16.4,21L15,22.4L12,19.4ZM5,12Q5,9.05 7.05,7.025Q9.1,5 12,5Q14.9,5 16.95,7.025Q19,9.05 19,12H17Q17,9.925 15.538,8.462Q14.075,7 12,7Q9.925,7 8.463,8.462Q7,9.925 7,12ZM1,12Q1,9.7 1.863,7.7Q2.725,5.7 4.225,4.212Q5.725,2.725 7.725,1.862Q9.725,1 12,1Q14.275,1 16.275,1.862Q18.275,2.725 19.775,4.212Q21.275,5.7 22.138,7.7Q23,9.7 23,12H21Q21,10.125 20.288,8.487Q19.575,6.85 18.35,5.625Q17.125,4.4 15.488,3.7Q13.85,3 12,3Q10.15,3 8.512,3.7Q6.875,4.4 5.65,5.625Q4.425,6.85 3.712,8.487Q3,10.125 3,12Z"/> +</vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/broadcast_dialog.xml b/packages/SystemUI/res/layout/broadcast_dialog.xml new file mode 100644 index 000000000000..5ba2afe5b172 --- /dev/null +++ b/packages/SystemUI/res/layout/broadcast_dialog.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2022 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/dialog_bg" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/broadcast_dialog_margin" + android:orientation="vertical"> + + <ImageView + android:id="@+id/dialog_icon" + android:layout_width="@dimen/broadcast_dialog_icon_size" + android:layout_height="@dimen/broadcast_dialog_icon_size" + android:layout_marginTop="@dimen/broadcast_dialog_icon_margin_top" + android:layout_marginBottom="@dimen/broadcast_dialog_title_img_margin_top" + android:layout_gravity="center" + android:src="@drawable/settings_input_antenna"/> + + <TextView + style="@style/BroadcastDialogTitleStyle" + android:id="@+id/dialog_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_gravity="center"/> + + <TextView + style="@style/BroadcastDialogBodyStyle" + android:id="@+id/dialog_subtitle" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_gravity="center"/> + </LinearLayout> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginHorizontal="@dimen/broadcast_dialog_margin" + android:layout_marginBottom="@dimen/broadcast_dialog_margin" + android:orientation="vertical"> + + <Button + android:layout_marginBottom="@dimen/broadcast_dialog_btn_margin_bottom" + android:id="@+id/switch_broadcast" + style="@style/BroadcastDialogButtonStyle"/> + + <Button + android:layout_marginBottom="@dimen/broadcast_dialog_btn_margin_bottom" + android:id="@+id/change_output" + android:text="@string/bt_le_audio_broadcast_dialog_different_output" + style="@style/BroadcastDialogButtonStyle"/> + + <Button + android:id="@+id/cancel" + android:text="@android:string/cancel" + style="@style/BroadcastDialogButtonStyle"/> + </LinearLayout> + +</LinearLayout> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 8ea838fc3c19..9a5c3237f41d 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Outo-draai"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Outodraai skerm"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ligging"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Sluimerskerm"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameratoegang"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofoontoegang"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Beskikbaar"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wiil jy jou sessie voortsit?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Begin van voor af"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, gaan voort"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Gasmodus"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Jy is in gasmodus"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"As ’n nuwe gebruiker bygevoeg word, sal gasmodus verlaat word en sal alle programme en data in die huidige gastesessie uitgevee word."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Gebruikerlimiet is bereik"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Jy kan tot <xliff:g id="COUNT">%d</xliff:g> gebruikers byvoeg.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Opletberigte"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Skermkiekies"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Algemene boodskappe"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Kitsprogramme"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Opstelling"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Berging"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Wenke"</string> <string name="instant_apps" msgid="8337185853050247304">"Kitsprogramme"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Wekker gestel"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera en mikrofoon is af"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# kennisgewing}other{# kennisgewings}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Uitsaai"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hou op om <xliff:g id="APP_NAME">%1$s</xliff:g> uit te saai?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"As jy <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitsaai of die uitvoer verander, sal jou huidige uitsending stop"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Saai <xliff:g id="SWITCHAPP">%1$s</xliff:g> uit"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Verander uitvoer"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Onbekend"</string> </resources> diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml index 93d26e8f1f83..e60f23329212 100644 --- a/packages/SystemUI/res/values-af/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Af"</item> <item msgid="460891964396502657">"Aan"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Onbeskikbaar"</item> + <item msgid="8014986104355098744">"Af"</item> + <item msgid="5966994759929723339">"Aan"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 45cccf8671e3..a344cf58ee11 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"በራስ ሰር አሽከርክር"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ማያ ገጽን በራስ-አሽከርክር"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"አካባቢ"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"የማያ ገጽ ማቆያ"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"የካሜራ መዳረሻ"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"የማይክሮፎን መዳረሻ"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ይገኛል"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ክፍለ-ጊዜዎን መቀጠል ይፈልጋሉ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"እንደገና ጀምር"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"አዎ፣ ቀጥል"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"የእንግዳ ሁነታ"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"በእንግዳ ሁኔታ ውስጥ ነዎት"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"አዲስ ተጠቃሚ ማከል ከእንግዳ ሁነታ ወጥቶ ሁሉንም መተግበሪያዎች እና ውሂብ አሁን ካለው የእንግዳ ክፍለ ጊዜ ይሰርዛል።"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"የተጠቃሚ ገደብ ላይ ተደርሷል"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one"><xliff:g id="COUNT">%d</xliff:g> ተጠቃሚዎች ብቻ ናቸው ሊፈጠሩ የሚችሉት።</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"ማንቂያዎች"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ባትሪ"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"ቅጽበታዊ ገጽ እይታዎች"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"አጠቃላይ መልዕክቶች"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"ቅጽበታዊ መተግበሪያዎች"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"ውቅረት"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"ማከማቻ"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"ፍንጮች"</string> <string name="instant_apps" msgid="8337185853050247304">"የቅጽበት መተግበሪያዎች"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ማንቂያ ተቀናብሯል"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ካሜራ እና ማይክሮፎን ጠፍተዋል"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ማሳወቂያ}one{# ማሳወቂያዎች}other{# ማሳወቂያዎች}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"በማሰራጨት ላይ"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>ን ማሰራጨት ይቁም?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>ን ካሰራጩ ወይም ውፅዓትን ከቀየሩ የአሁኑ ስርጭትዎ ይቆማል"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ያሰራጩ"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ውፅዓትን ይቀይሩ"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ያልታወቀ"</string> </resources> diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml index 12be1ae2fd22..bbf2d2385f05 100644 --- a/packages/SystemUI/res/values-am/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"አጥፋ"</item> <item msgid="460891964396502657">"አብራ"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"አይገኝም"</item> + <item msgid="8014986104355098744">"ጠፍቷል"</item> + <item msgid="5966994759929723339">"በርቷል"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 0344a52164c7..49fe53a5bd7c 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -230,6 +230,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"التدوير التلقائي"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"التدوير التلقائي للشاشة"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"الموقع الجغرافي"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"شاشة الاستراحة"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"الوصول إلى الكاميرا"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"الوصول إلى الميكروفون"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"متاح"</string> @@ -359,6 +360,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"هل تريد متابعة جلستك؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"البدء من جديد"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"نعم، متابعة"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"وضع الضيف"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"أنت تستخدِم وضع الضيف."</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ستؤدي إضافة مُستخدِم جديد إلى الخروج من وضع الضيف وحذف كل التطبيقات والبيانات من جلسة الضيف الحالية."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"تم الوصول إلى أقصى عدد للمستخدمين"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="zero">يمكنك إضافة ما يصل إلى <xliff:g id="COUNT">%d</xliff:g> مستخدم.</item> @@ -717,7 +721,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"التنبيهات"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"البطارية"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"لقطات الشاشة"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"رسائل عامة"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"التطبيقات الفورية"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"عملية الإعداد"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"مساحة التخزين"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"تلميحات"</string> <string name="instant_apps" msgid="8337185853050247304">"التطبيقات الفورية"</string> @@ -988,4 +993,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"تم ضبط المنبه."</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"الكاميرا والميكروفون غير مفعّلين."</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{إشعار واحد}zero{# إشعار}two{إشعاران}few{# إشعارات}many{# إشعارًا}other{# إشعار}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"البث"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"هل تريد إيقاف بث تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>؟"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"إذا أجريت بث تطبيق <xliff:g id="SWITCHAPP">%1$s</xliff:g> أو غيَّرت جهاز الإخراج، سيتوقَف البث الحالي."</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"بث تطبيق <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"تغيير جهاز الإخراج"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"غير معروف"</string> </resources> diff --git a/packages/SystemUI/res/values-ar/tiles_states_strings.xml b/packages/SystemUI/res/values-ar/tiles_states_strings.xml index b4fb760f5e65..44b58f964ce9 100644 --- a/packages/SystemUI/res/values-ar/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ar/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"غير مفعّل"</item> <item msgid="460891964396502657">"مفعّل"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"غير متوفّرة"</item> + <item msgid="8014986104355098744">"غير مفعّلة"</item> + <item msgid="5966994759929723339">"مفعَّلة"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 3ff96c2641de..ea1293f4d3b8 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"স্বয়ং-ঘূৰ্ণন"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"স্বয়ং-ঘূৰ্ণন স্ক্ৰীন"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"অৱস্থান"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"স্ক্ৰীন ছেভাৰ"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"কেমেৰাৰ এক্সেছ"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"মাইকৰ এক্সেছ"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"উপলব্ধ"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপুনি আপোনাৰ ছেশ্বন অব্যাহত ৰাখিব বিচাৰেনে?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আকৌ আৰম্ভ কৰক"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"হয়, অব্যাহত ৰাখক"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"অতিথি ম’ড"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"আপুনি অতিথি ম’ডত আছে"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"এগৰাকী নতুন ব্যৱহাৰকাৰীক যোগ দিয়াটোৱে অতিথি ম’ডৰ পৰা বাহিৰ কৰিব আৰু বৰ্তমানৰ অতিথিৰ ছেশ্বনটোৰ পৰা আটাইবোৰ এপ্ আৰু ডেটা মচিব।"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"অধিকতম ব্যৱহাৰকাৰী সৃষ্টি কৰা হ’ল"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">আপুনি <xliff:g id="COUNT">%d</xliff:g> জনলৈকে ব্যৱহাৰকাৰী যোগ কৰিব পাৰে।</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"সতৰ্কবার্তাসমূহ"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"বেটাৰী"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"স্ক্ৰীণশ্বটসমূহ"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"সাধাৰণ বার্তাসমূহ"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"তাৎক্ষণিক এপ্"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"ছেটআপ"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"ষ্ট\'ৰেজ"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"ইংগিতবোৰ"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"এলাৰ্ম ছেট কৰা হ’ল"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"কেমেৰা আৰু মাইক অফ হৈ আছে"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# টা জাননী}one{# টা জাননী}other{# টা জাননী}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"সম্প্ৰচাৰণ"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ সম্প্ৰচাৰ কৰা বন্ধ কৰিবনে?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"যদি আপুনি <xliff:g id="SWITCHAPP">%1$s</xliff:g>ৰ সম্প্ৰচাৰ কৰে অথবা আউটপুট সলনি কৰে, তেন্তে, আপোনাৰ বৰ্তমানৰ সম্প্ৰচাৰ বন্ধ হৈ যাব"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> সম্প্ৰচাৰ কৰক"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"আউটপুট সলনি কৰক"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"অজ্ঞাত"</string> </resources> diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml index 7767cfc6943a..3145341cabe4 100644 --- a/packages/SystemUI/res/values-as/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"অফ"</item> <item msgid="460891964396502657">"অন"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"উপলব্ধ নহয়"</item> + <item msgid="8014986104355098744">"অফ আছে"</item> + <item msgid="5966994759929723339">"অন আছে"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index e9f2977eccbc..678da7f442ff 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avtodönüş"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranın avtomatik dönməsi"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Məkan"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekran qoruyucu"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraya giriş"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofona giriş"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Əlçatan"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Sessiya davam etsin?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Yenidən başlayın"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Bəli, davam edin"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Qonaq rejimi"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Qonaq rejimindəsiniz"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Yeni istifadəçi əlavə edildikdə qonaq rejimindən çıxılacaq və cari qonaq sessiyasındakı bütün tətbiqlər və data silinəcək."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"İstifadəçi limitinə çatmısınız"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Maksimum <xliff:g id="COUNT">%d</xliff:g> istifadəçi əlavə edə bilərsiniz.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Xəbərdarlıqlar"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batareya"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Skrinşotlar"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Ümumi Mesajlar"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Ani Tətbiqlər"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Ayarlama"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Yaddaş"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Məsləhətlər"</string> <string name="instant_apps" msgid="8337185853050247304">"Ani Tətbiqlər"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Siqnal ayarlanıb"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera və mikrofon deaktivdir"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# bildiriş}other{# bildiriş}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Yayım"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqinin yayımlanması dayandırılsın?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> tətbiqini yayımlasanız və ya nəticəni dəyişsəniz, cari yayımınız dayandırılacaq"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> tətbiqini yayımlayın"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Nəticəni dəyişdirin"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Naməlum"</string> </resources> diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml index 0311794de3cd..fb745b251bc9 100644 --- a/packages/SystemUI/res/values-az/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Deaktiv"</item> <item msgid="460891964396502657">"Aktiv"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Əlçatmazdır"</item> + <item msgid="8014986104355098744">"Deaktiv"</item> + <item msgid="5966994759929723339">"Aktiv"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index b9bab1e50925..4baa16e0b32c 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -227,6 +227,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatska rotacija"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Čuvar ekrana"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Pristup kameri"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Pristup mikrofonu"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dostupno"</string> @@ -353,6 +354,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li da nastavite sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni iz početka"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nastavi"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Režim gosta"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Koristite režim gosta"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dodavanjem novog korisnika izaći ćete iz režima gosta i izbrisaćete sve aplikacije i podatke iz aktuelne sesije gosta."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Dostignut maksimalni broj korisnika"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Možete da dodate najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item> @@ -702,7 +706,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Obaveštenja"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Baterija"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Snimci ekrana"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Opšte poruke"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant aplikacije"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Podešavanje"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Memorijski prostor"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Saveti"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant aplikacije"</string> @@ -967,4 +972,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je podešen"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera i mikrofon su isključeni"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obaveštenje}one{# obaveštenje}few{# obaveštenja}other{# obaveštenja}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitovanje"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Želite da zaustavite emitovanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitujete aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promenite izlaz, aktuelno emitovanje će se zaustaviti"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitujte aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Promenite izlaz"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nepoznato"</string> </resources> diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml index a057c48bbec9..b69b06419281 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Isključeno"</item> <item msgid="460891964396502657">"Uključeno"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Nedostupno"</item> + <item msgid="8014986104355098744">"Isključeno"</item> + <item msgid="5966994759929723339">"Uključeno"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 88ee0804beb8..2d59fa4bf2aa 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -228,6 +228,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аўтапаварот"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аўтаматычны паварот экрана"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Месцазнаходжанне"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Экранная застаўка"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Доступ да камеры"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Доступ да мікрафона"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Доступ дазволены"</string> @@ -355,6 +356,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Так, працягнуць"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Гасцявы рэжым"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Вы знаходзіцеся ў гасцявым рэжыме"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Дадаванне новага карыстальніка закрые гасцявы рэжым. Будуць выдалены ўсе праграмы і даныя бягучага гасцявога сеанса."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Дасягнуты ліміт карыстальнікаў"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Можна дадаць <xliff:g id="COUNT">%d</xliff:g> карыстальніка.</item> @@ -707,7 +711,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Абвесткі"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Акумулятар"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Здымкі экрана"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Агульныя паведамленні"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Імгненныя праграмы"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Наладжванне"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Захоўванне"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Падказкі"</string> <string name="instant_apps" msgid="8337185853050247304">"Імгненныя праграмы"</string> @@ -974,4 +979,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будзільнік зададзены"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера і мікрафон выключаны"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# апавяшчэнне}one{# апавяшчэнне}few{# апавяшчэнні}many{# апавяшчэнняў}other{# апавяшчэння}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Перадача даных"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Спыніць трансляцыю праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Пры пераключэнні на праграму \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\" ці змяненні вываду бягучая трансляцыя спыняецца"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Трансляцыя праграмы \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\""</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Змяненне вываду"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Невядома"</string> </resources> diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml index d2588113f37b..8fb2da26edc2 100644 --- a/packages/SystemUI/res/values-be/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Выключана"</item> <item msgid="460891964396502657">"Уключана"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Недаступна"</item> + <item msgid="8014986104355098744">"Выключана"</item> + <item msgid="5966994759929723339">"Уключана"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 8e5bf4610098..2d91d4a5622a 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматична ориентация"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично завъртане на екрана"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Местоположение"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Скрийнсейвър"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Достъп до камерата"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Достъп до микрофона"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Налице"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Искате ли да продължите сесията си?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Започване отначало"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, продължавам"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Режим на гост"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Вие сте в режим на гост"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"С добавянето на нов потребител ще излезете от режима на гост и ще изтриете всички приложения и данни от текущата сесия като гост."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнахте огранич. за потребители"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Можете да добавите до <xliff:g id="COUNT">%d</xliff:g> потребители.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Сигнали"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Батерия"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Екранни снимки"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Общи съобщения"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Мигновени приложения"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Настройване"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Хранилище"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Съвети"</string> <string name="instant_apps" msgid="8337185853050247304">"Мигновени приложения"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будилникът е зададен"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камерата и микрофонът са изключени"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# известие}other{# известия}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Излъчване"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Да се спре ли предаването на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако предавате <xliff:g id="SWITCHAPP">%1$s</xliff:g> или промените изхода, текущото ви предаване ще бъде прекратено"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Предаване на <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Промяна на изхода"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Неизвестно"</string> </resources> diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml index da5c325aaa1c..b85133b6bd43 100644 --- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Изкл."</item> <item msgid="460891964396502657">"Вкл."</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Не е налице"</item> + <item msgid="8014986104355098744">"Изкл."</item> + <item msgid="5966994759929723339">"Вкл."</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 11cf9ff37766..be51d0630629 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"নিজে থেকে ঘুরবে"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"অটো-রোটেট স্ক্রিন"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"লোকেশন"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"স্ক্রিন সেভার"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"ক্যামেরা অ্যাক্সেস"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"মাইক্রোফোন অ্যাক্সেস"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"উপলভ্য"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপনি কি আপনার সেশনটি চালিয়ে যেতে চান?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আবার শুরু করুন"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"হ্যাঁ, চালিয়ে যান"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"অতিথি মোড"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"আপনি \'অতিথি মোড\' ব্যবহার করছেন"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"নতুন ব্যবহারকারী যোগ করার মাধ্যমে \'অতিথি মোড\' ছেড়ে বেরিয়ে আসতে পারবেন এবং বর্তমান অতিথি সেশন থেকে সব অ্যাপ ও ডেটা মুছে যাবে।"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"আর কোনও প্রোফাইল যোগ করা যাবে না"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">আপনি <xliff:g id="COUNT">%d</xliff:g> জন পর্যন্ত ব্যবহারকারীর প্রোফাইল যোগ করতে পারেন।</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"সতর্কতা"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ব্যাটারি"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"স্ক্রীনশটস"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"সাধারণ বার্তাগুলি"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"সেট-আপ"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"স্টোরেজ"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"হিন্ট"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"অ্যালার্ম সেট করা হয়েছে"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ক্যামেরা ও মাইক্রোফোন বন্ধ আছে"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{#টি বিজ্ঞপ্তি}one{#টি বিজ্ঞপ্তি}other{#টি বিজ্ঞপ্তি}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ব্রডকাস্ট করা হচ্ছে"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> সম্প্রচার বন্ধ করবেন?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"আপনি <xliff:g id="SWITCHAPP">%1$s</xliff:g> সম্প্রচার করলে বা আউটপুট পরিবর্তন করলে, আপনার বর্তমান সম্প্রচার বন্ধ হয়ে যাবে"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> সম্প্রচার করুন"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"আউটপুট পরিবর্তন করুন"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"অজানা"</string> </resources> diff --git a/packages/SystemUI/res/values-bn/tiles_states_strings.xml b/packages/SystemUI/res/values-bn/tiles_states_strings.xml index 784d974e2eba..d70afc0f7f4f 100644 --- a/packages/SystemUI/res/values-bn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bn/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"বন্ধ আছে"</item> <item msgid="460891964396502657">"চালু আছে"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"অনুপলভ্য"</item> + <item msgid="8014986104355098744">"বন্ধ আছে"</item> + <item msgid="5966994759929723339">"চালু আছে"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index d0830c48b272..e8d735c205ec 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -227,6 +227,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko rotiranje"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko rotiranje ekrana"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Čuvar ekrana"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Pristup kameri"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Pristup mikrofonu"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dostupno"</string> @@ -353,6 +354,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nastavi"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Način rada za gosta"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Nalazite se u načinu rada za gosta"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dodavanjem novog korisnika napustit ćete način rada za gosta i izbrisati sve aplikacije i podatke iz trenutne sesije gosta."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Dostignut limit za broj korisnika"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item> @@ -702,7 +706,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Obavještenja"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Baterija"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Snimci ekrana"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Opće poruke"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant aplikacije"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Postavljanje"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Pohrana"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Savjeti"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant aplikacije"</string> @@ -967,4 +972,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je postavljen"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera i mikrofon su isključeni"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obavještenje}one{# obavještenje}few{# obavještenja}other{# obavještenja}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiranje"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zaustaviti emitiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitirate aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promijenite izlaz, trenutno emitiranje će se zaustaviti"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitiraj aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Promijeni izlaz"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nepoznato"</string> </resources> diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml index a057c48bbec9..b69b06419281 100644 --- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Isključeno"</item> <item msgid="460891964396502657">"Uključeno"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Nedostupno"</item> + <item msgid="8014986104355098744">"Isključeno"</item> + <item msgid="5966994759929723339">"Uključeno"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 678a3d63b427..a9a115ba4523 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Gira automàticament"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Gira la pantalla automàticament"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicació"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Estalvi de pantalla"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Accés a la càmera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Accés al micròfon"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vols continuar amb la sessió?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Torna a començar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sí, continua"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Mode de convidat"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Estàs en mode de convidat"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"En afegir un usuari nou, se sortirà del mode de convidat i se suprimiran totes les aplicacions i dades de la sessió de convidat actual."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"S\'ha assolit el límit d\'usuaris"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Pots afegir fins a <xliff:g id="COUNT">%d</xliff:g> usuaris.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alertes"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Captures de pantalla"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Missatges generals"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Aplicacions instantànies"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configuració"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Emmagatzematge"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Suggeriments"</string> <string name="instant_apps" msgid="8337185853050247304">"Aplicacions instantànies"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma definida"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Càmera i micròfon desactivats"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificació}other{# notificacions}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"S\'està emetent"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vols deixar d\'emetre <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si emets <xliff:g id="SWITCHAPP">%1$s</xliff:g> o canvies la sortida, l\'emissió actual s\'aturarà"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emet <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Canvia la sortida"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconeguda"</string> </resources> diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml index 2d13e568d5f3..aaf19c7c0cc6 100644 --- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Desactivat"</item> <item msgid="460891964396502657">"Activat"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"No disponible"</item> + <item msgid="8014986104355098744">"Desactivat"</item> + <item msgid="5966994759929723339">"Activat"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index a99c5c20803d..0357e171058f 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -228,6 +228,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčení"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otočení obrazovky"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Spořič obrazovky"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Přístup k fotoaparátu"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Přístup k mikrofonu"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dostupné"</string> @@ -355,6 +356,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relaci pokračovat?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začít znovu"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ano, pokračovat"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Režim hosta"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Jste v režimu hosta"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Přidáním nového uživatele ukončíte režim hosta a smažete všechny aplikace a data z aktuální relace hosta."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Bylo dosaženo limitu uživatelů"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="few">Lze přidat až <xliff:g id="COUNT">%d</xliff:g> uživatele.</item> @@ -707,7 +711,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Upozornění"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Baterie"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Snímky obrazovek"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Všeobecné zprávy"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Okamžité aplikace"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Nastavit"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Úložiště"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Tipy"</string> <string name="instant_apps" msgid="8337185853050247304">"Okamžité aplikace"</string> @@ -974,4 +979,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Je nastaven budík"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotoaparát a mikrofon jsou vypnuté"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# oznámení}few{# oznámení}many{# oznámení}other{# oznámení}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Vysílání"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zastavit vysílání v aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Pokud budete vysílat v aplikaci <xliff:g id="SWITCHAPP">%1$s</xliff:g> nebo změníte výstup, aktuální vysílání se zastaví"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Vysílat v aplikaci <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Změna výstupu"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Neznámé"</string> </resources> diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml index 2af84d97653c..64e83e0c31b8 100644 --- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Vypnuto"</item> <item msgid="460891964396502657">"Zapnuto"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Nedostupné"</item> + <item msgid="8014986104355098744">"Vypnuto"</item> + <item msgid="5966994759929723339">"Zapnuto"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index ca5a257ca60b..6d2c3d2d9ee9 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Roter automatisk"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Roter skærmen automatisk"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokation"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Pauseskærm"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraadgang"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonadgang"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tilgængelig"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsætte din session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start forfra"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, fortsæt"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Gæstetilstand"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Gæstetilstand er aktiveret"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Hvis du tilføjer en ny bruger, deaktiveres gæstetilstanden, og alle apps og data slettes fra den aktuelle gæstesession."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Grænsen for antal brugere er nået"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Du kan tilføje op til <xliff:g id="COUNT">%d</xliff:g> bruger.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Underretninger"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batteri"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Generelle meddelelser"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Konfiguration"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Lagerplads"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Tips"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmen er indstillet"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera og mikrofon er slået fra"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifikation}one{# notifikation}other{# notifikationer}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Udsender"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop udsendelsen <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Hvis du udsender <xliff:g id="SWITCHAPP">%1$s</xliff:g> eller skifter output, stopper din aktuelle udsendelse"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Udsend <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Skift output"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ukendt"</string> </resources> diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml index 0fe06b3433c3..f0132dc66130 100644 --- a/packages/SystemUI/res/values-da/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Fra"</item> <item msgid="460891964396502657">"Til"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Ikke tilgængelig"</item> + <item msgid="8014986104355098744">"Fra"</item> + <item msgid="5966994759929723339">"Til"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 5b555fc5bc59..3b11ffc20755 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. drehen"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Bildschirm automatisch drehen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Standort"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Bildschirmschoner"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kamerazugriff"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonzugriff"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Verfügbar"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Möchtest du deine Sitzung fortsetzen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Neu starten"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, weiter"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Gastmodus"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Du befindest dich im Gastmodus"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Durch Hinzufügen eines neuen Nutzers wird der Gastmodus beendet und alle Apps und Daten der aktuellen Gastsitzung werden gelöscht."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Nutzerlimit erreicht"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Du kannst bis zu <xliff:g id="COUNT">%d</xliff:g> Nutzer hinzufügen.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Benachrichtigungen"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Akku"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Nachrichten"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Android Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Einrichtung"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Speicher"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Hinweise"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Wecker gestellt"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera und Mikrofon ausgeschaltet"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# Benachrichtigung}other{# Benachrichtigungen}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Übertragung läuft"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Wenn du <xliff:g id="SWITCHAPP">%1$s</xliff:g> streamst oder die Ausgabe änderst, wird dein aktueller Stream beendet"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> streamen"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ausgabe ändern"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unbekannt"</string> </resources> diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml index ba610b33ccfa..bc50e1603ea4 100644 --- a/packages/SystemUI/res/values-de/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Aus"</item> <item msgid="460891964396502657">"An"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Nicht verfügbar"</item> + <item msgid="8014986104355098744">"Aus"</item> + <item msgid="5966994759929723339">"An"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index a0d20528c660..e3eea2a4e328 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Αυτόματη περιστροφή"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Αυτόματη περιστροφή οθόνης"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Τοποθεσία"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Προφύλαξη οθόνης"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Πρόσβαση κάμερας"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Πρόσβαση μικροφώνου"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Διαθέσιμη"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Θέλετε να συνεχίσετε την περίοδο σύνδεσής σας;"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Έναρξη από την αρχή"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ναι, συνέχεια"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Λειτουργία επισκέπτη"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Βρίσκεστε σε λειτουργία επισκέπτη"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Με την προσθήκη νέου χρήστη θα γίνει έξοδος από τη λειτουργία επισκέπτη και θα διαγραφούν όλες οι εφαρμογές και τα δεδομένα από την τρέχουσα περίοδο σύνδεσης επισκέπτη."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Συμπληρώθηκε το όριο χρηστών"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Μπορείτε να προσθέσετε έως <xliff:g id="COUNT">%d</xliff:g> χρήστες.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Ειδοποιήσεις"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Μπαταρία"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Στιγμιότυπα οθόνης"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Γενικά μηνύματα"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Εφαρμογές"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Ρύθμιση"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Αποθηκευτικός χώρος"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Συμβουλές"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Εφαρμογές"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Το ξυπνητήρι ρυθμίστηκε"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Η κάμερα και το μικρόφωνο έχουν απενεργοποιηθεί"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ειδοποίηση}other{# ειδοποιήσεις}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Μετάδοση"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Διακοπή μετάδοσης με την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Εάν κάνετε μετάδοση με την εφαρμογή <xliff:g id="SWITCHAPP">%1$s</xliff:g> ή αλλάξετε την έξοδο, η τρέχουσα μετάδοση θα σταματήσει"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Μετάδοση με την εφαρμογή <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Αλλαγή εξόδου"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Άγνωστο"</string> </resources> diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml index 74c091e4ed18..352af39bfe11 100644 --- a/packages/SystemUI/res/values-el/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Ανενεργή"</item> <item msgid="460891964396502657">"Ενεργή"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Μη διαθέσιμο"</item> + <item msgid="8014986104355098744">"Ανενεργό"</item> + <item msgid="5966994759929723339">"Ενεργό"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 82bcb31a4300..1777dadde803 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Camera access"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mic access"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alerts"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"General Messages"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera and mic are off"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml index 6f8cfb695ac6..56cdbef092f2 100644 --- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Off"</item> <item msgid="460891964396502657">"On"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Unavailable"</item> + <item msgid="8014986104355098744">"Off"</item> + <item msgid="5966994759929723339">"On"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 5d413707ca11..3f04c2e0b2e8 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Camera access"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mic access"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alerts"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"General Messages"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera and mic are off"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml index 6f8cfb695ac6..56cdbef092f2 100644 --- a/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rCA/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Off"</item> <item msgid="460891964396502657">"On"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Unavailable"</item> + <item msgid="8014986104355098744">"Off"</item> + <item msgid="5966994759929723339">"On"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 82bcb31a4300..1777dadde803 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Camera access"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mic access"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alerts"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"General Messages"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera and mic are off"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml index 6f8cfb695ac6..56cdbef092f2 100644 --- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Off"</item> <item msgid="460891964396502657">"On"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Unavailable"</item> + <item msgid="8014986104355098744">"Off"</item> + <item msgid="5966994759929723339">"On"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 82bcb31a4300..1777dadde803 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Camera access"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mic access"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Adding a new user will exit guest mode and delete all apps and data from the current guest session."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alerts"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"General Messages"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera and mic are off"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml index 6f8cfb695ac6..56cdbef092f2 100644 --- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Off"</item> <item msgid="460891964396502657">"On"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Unavailable"</item> + <item msgid="8014986104355098744">"Off"</item> + <item msgid="5966994759929723339">"On"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 98f5c83672d3..f7415b25d6b6 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Auto-rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Auto-rotate screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Location"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screen saver"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Camera access"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mic access"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start over"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yes, continue"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"You are in guest mode"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">"\n\nAdding a new user will exit guest mode and delete all apps and data from the current guest session."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"User limit reached"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">You can add up to <xliff:g id="COUNT">%d</xliff:g> users.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alerts"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Battery"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"General Messages"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm set"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera and mic are off"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}other{# notifications}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Broadcasting"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Stop broadcasting <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"If you broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g> or change the output, your current broadcast will stop"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Broadcast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Change output"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> </resources> diff --git a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml index 99ef50c9d8af..3a8e34c2ebdc 100644 --- a/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rXC/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Off"</item> <item msgid="460891964396502657">"On"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Unavailable"</item> + <item msgid="8014986104355098744">"Off"</item> + <item msgid="5966994759929723339">"On"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 88c38ee9b8e1..509240e8961a 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar la pantalla automáticamente"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Protector de pantalla"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Acceso a la cámara"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Acceso al mic."</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres retomar la sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sí, continuar"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo de Invitado"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Estás en el modo de invitado"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si agregas un usuario nuevo, se desactivará el modo de invitado y se borrarán todas las apps y los datos de la sesión de invitado actual."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Alcanzaste el límite de usuarios"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Puedes agregar hasta <xliff:g id="COUNT">%d</xliff:g> usuarios.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batería"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de pantalla"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mensajes generales"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Apps instantáneas"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configuración"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Almacenamiento"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Sugerencias"</string> <string name="instant_apps" msgid="8337185853050247304">"Apps instantáneas"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Se estableció la alarma"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"La cámara y el micrófono están apagados"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}other{# notificaciones}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitiendo"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"¿Quieres dejar de transmitir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si transmites <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambias la salida, tu transmisión actual se detendrá"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambia la salida"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconocido"</string> </resources> diff --git a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml index 6b572e4920c6..2dca6109d5d1 100644 --- a/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-es-rUS/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Desactivado"</item> <item msgid="460891964396502657">"Sí"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"No disponible"</item> + <item msgid="8014986104355098744">"No"</item> + <item msgid="5966994759929723339">"Sí"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 9b65bb1392a7..a7048839ab4a 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Girar pantalla automáticamente"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Ubicación"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Salvapantallas"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Acceso a cámara"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Acceso al micro"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres continuar con tu sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sí, continuar"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo Invitado"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Estás en modo Invitado"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si añades un nuevo usuario, saldrás del modo Invitado y se eliminarán todas las aplicaciones y datos de la sesión de invitado actual."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Has alcanzado el límite de usuarios"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Puedes añadir hasta <xliff:g id="COUNT">%d</xliff:g> usuarios.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batería"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de pantalla"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mensajes generales"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Aplicaciones Instantáneas"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configuración"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Almacenamiento"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Sugerencias"</string> <string name="instant_apps" msgid="8337185853050247304">"Aplicaciones Instantáneas"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma añadida"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"La cámara y el micrófono están desactivados"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}other{# notificaciones}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiendo"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"¿Dejar de emitir <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si emites <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambias la salida, tu emisión actual se detendrá"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambiar salida"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconocido"</string> </resources> diff --git a/packages/SystemUI/res/values-es/tiles_states_strings.xml b/packages/SystemUI/res/values-es/tiles_states_strings.xml index e4310af3d531..36a542a5ad67 100644 --- a/packages/SystemUI/res/values-es/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-es/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Desactivado"</item> <item msgid="460891964396502657">"Activado"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"No disponible"</item> + <item msgid="8014986104355098744">"Desactivado"</item> + <item msgid="5966994759929723339">"Activado"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 58ac8db80289..66933fcf5e04 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. pööramine"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Kuva automaatne pööramine"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Asukoht"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekraanisäästja"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Juurdepääs kaamerale"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Juurdepääs mikrofonile"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Saadaval"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Jah, jätka"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Külalisrežiim"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Olete külalisrežiimis"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Uue kasutaja lisamisel suletakse külalisrežiim ning praeguse külastajaseansi rakendused ja andmed kustutatakse."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Kasutajate limiit on täis"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Võite lisada kuni <xliff:g id="COUNT">%d</xliff:g> kasutajat.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Hoiatused"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Aku"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Ekraanipildid"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Üldised sõnumid"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Installimata avatavad rakendused"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Seadistamine"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Salvestusruum"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Vihjed"</string> <string name="instant_apps" msgid="8337185853050247304">"Installimata avatavad rakendused"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm on määratud"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kaamera ja mikrofon on välja lülitatud"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# märguanne}other{# märguannet}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Edastamine"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Kas peatada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> ülekandmine?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Kui kannate rakendust <xliff:g id="SWITCHAPP">%1$s</xliff:g> üle või muudate väljundit, peatatakse teie praegune ülekanne"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Rakenduse <xliff:g id="SWITCHAPP">%1$s</xliff:g> ülekandmine"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Väljundi muutmine"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tundmatu"</string> </resources> diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml index 29895d1a437a..07eddef9383e 100644 --- a/packages/SystemUI/res/values-et/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Väljas"</item> <item msgid="460891964396502657">"Sees"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Pole saadaval"</item> + <item msgid="8014986104355098744">"Väljas"</item> + <item msgid="5966994759929723339">"Sees"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index f580c960b553..39de3677fbe6 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Biratze automatikoa"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Biratu pantaila automatikoki"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Kokapena"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Pantaila-babeslea"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kamera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonoa"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Baimenduta"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Saioarekin jarraitu nahi duzu?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Hasi berriro"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Bai, jarraitu"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Gonbidatu modua"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Gonbidatu moduan zaude"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Beste erabiltzaile bat gehituz gero, gonbidatu modua itxiko da eta oraingo gonbidatuentzako saioko aplikazio eta datu guztiak ezabatuko dira."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Erabiltzaile-mugara iritsi zara"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Gehienez, <xliff:g id="COUNT">%d</xliff:g> erabiltzaile gehi ditzakezu.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alertak"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Pantaila-argazkiak"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mezu orokorrak"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Zuzeneko aplikazioak"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Konfigurazioa"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Memoria"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Aholkuak"</string> <string name="instant_apps" msgid="8337185853050247304">"Zuzeneko aplikazioak"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma ezarrita dago"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera eta mikrofonoa desaktibatuta daude"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# jakinarazpen}other{# jakinarazpen}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Igortzen"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioaren audioa igortzeari utzi nahi diozu?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> aplikazioaren audioa igortzen baduzu, edo audio-irteera aldatzen baduzu, une hartako igorpena eten egingo da"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Igorri <xliff:g id="SWITCHAPP">%1$s</xliff:g> aplikazioaren audioa"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Aldatu audio-irteera"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ezezaguna"</string> </resources> diff --git a/packages/SystemUI/res/values-eu/tiles_states_strings.xml b/packages/SystemUI/res/values-eu/tiles_states_strings.xml index baddea16b833..3bf49c8e0c77 100644 --- a/packages/SystemUI/res/values-eu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-eu/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Desaktibatuta"</item> <item msgid="460891964396502657">"Aktibatuta"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Ez dago erabilgarri"</item> + <item msgid="8014986104355098744">"Desaktibatuta"</item> + <item msgid="5966994759929723339">"Aktibatuta"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index c3a84c21b890..04b4a9e0af0a 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"چرخش خودکار"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"چرخش خودکار صفحهنمایش"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"مکان"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"محافظ صفحه"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"دسترسی به دوربین"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"دسترسی به میکروفون"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"دردسترس"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"آیا میخواهید جلسهتان را ادامه دهید؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"شروع مجدد"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"بله، ادامه داده شود"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"حالت مهمان"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"در حالت مهمان هستید"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"با افزودن کاربر جدید، از حالت مهمان خارج خواهید شد و همه برنامهها و دادهها از جلسه مهمان کنونی حذف خواهند شد."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"به تعداد مجاز تعداد کاربر رسیدهاید"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">میتوانید حداکثر <xliff:g id="COUNT">%d</xliff:g> کاربر اضافه کنید.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"هشدارها"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"باتری"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"نماگرفتها"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"پیامهای عمومی"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"برنامههای فوری"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"راهاندازی"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"فضای ذخیرهسازی"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"نکات"</string> <string name="instant_apps" msgid="8337185853050247304">"برنامههای فوری"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"زنگ ساعت تنظیم شد"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"دوربین و میکروفون خاموش هستند"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# اعلان}one{# اعلان}other{# اعلان}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"همهفرستی"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"همهفرستی <xliff:g id="APP_NAME">%1$s</xliff:g> متوقف شود؟"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"اگر <xliff:g id="SWITCHAPP">%1$s</xliff:g> را همهفرستی کنید یا خروجی را تغییر دهید، همهفرستی کنونی متوقف خواهد شد"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"همهفرستی <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"تغییر خروجی"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"نامشخص"</string> </resources> diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml index ecc8d1cc7b13..85f0bfdf86dd 100644 --- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"خاموش"</item> <item msgid="460891964396502657">"روشن"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"دردسترس نیست"</item> + <item msgid="8014986104355098744">"خاموش"</item> + <item msgid="5966994759929723339">"روشن"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 54e10bf5c5b3..9c4a6cacc207 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automaattinen kääntö"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Käännä näyttöä automaattisesti."</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Sijainti"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Näytönsäästäjä"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Pääsy kameraan"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Pääsy mikrofoniin"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Käytettävissä"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Haluatko jatkaa istuntoa?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Aloita alusta"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Kyllä, haluan jatkaa"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Vierastila"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Olet vierastilassa"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Uuden käyttäjän lisääminen poistaa sinut vierastilasta. Kaikki sovellukset ja data poistetaan myös samalla nykyisestä vierailija-käyttökerrasta."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Käyttäjäraja saavutettu"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Voit lisätä korkeintaan <xliff:g id="COUNT">%d</xliff:g> käyttäjää.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Ilmoitukset"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Akku"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Kuvakaappaukset"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Yleiset viestit"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Määritys"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Tallennustila"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Vihjeet"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Hälytys asetettu"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera ja mikrofoni ovat pois päältä"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ilmoitus}other{# ilmoitusta}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Lähettää"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Lopetetaanko <xliff:g id="APP_NAME">%1$s</xliff:g>-sovelluksen lähettäminen?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jos lähetät <xliff:g id="SWITCHAPP">%1$s</xliff:g>-sovellusta tai muutat ulostuloa, nykyinen lähetyksesi loppuu"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Lähetä <xliff:g id="SWITCHAPP">%1$s</xliff:g>-sovellusta"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Muuta ulostuloa"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tuntematon"</string> </resources> diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml index 5844fb9b79c5..1505dc5c06bb 100644 --- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Poissa päältä"</item> <item msgid="460891964396502657">"Päällä"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Ei saatavilla"</item> + <item msgid="8014986104355098744">"Poissa päältä"</item> + <item msgid="5966994759929723339">"Päällä"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 693a0758cf29..a0abc35aaad4 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation automatique"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Écran de veille"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès à l\'appareil photo"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Accès au micro"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Accessible"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recommencer"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Oui, continuer"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Mode Invité"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Vous êtes en mode Invité"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si vous ajoutez un nouvel utilisateur, vous quitterez le mode Invité, et toutes les applications et données de la session d\'invité en cours seront supprimées."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite d\'utilisateurs atteinte"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Vous pouvez ajouter jusqu\'à <xliff:g id="COUNT">%d</xliff:g> utilisateur.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alertes"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Pile"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Saisies d\'écran"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Messages généraux"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Applications instantanées"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configuration"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Stockage"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Conseils"</string> <string name="instant_apps" msgid="8337185853050247304">"Applications instantanées"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"L\'alarme a été réglée"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"L\'appareil photo et le micro sont désactivés"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}other{# notifications}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Diffusion en cours…"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou changez la sortie, votre diffusion actuelle s\'arrêtera"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Diffuser <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Changer la sortie"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Inconnue"</string> </resources> diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml index 9d78e91f0f69..4ec00846283a 100644 --- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Désactivé"</item> <item msgid="460891964396502657">"Activé"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Non accessible"</item> + <item msgid="8014986104355098744">"Désactivé"</item> + <item msgid="5966994759929723339">"Activé"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index b7d92bbed9e3..25bccba384fe 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotation automatique"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotation automatique de l\'écran"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localisation"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Économiseur d\'écran"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Accès à l\'appareil photo"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Accès au micro"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponible"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la dernière session ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Non, nouvelle session"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Oui, continuer"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Mode Invité"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Vous êtes en mode Invité"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Si vous ajoutez un utilisateur, le mode Invité sera désactivé et toutes les applis et les données de la session Invité actuelle seront supprimées."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite nombre utilisateurs atteinte"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Vous pouvez ajouter <xliff:g id="COUNT">%d</xliff:g> profil utilisateur.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alertes"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batterie"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Captures d\'écran"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Nouveaux messages"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Applis instantanées"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configurer"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Espace de stockage"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Astuces"</string> <string name="instant_apps" msgid="8337185853050247304">"Applis instantanées"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme réglée"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Appareil photo et micro désactivés"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}other{# notifications}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Diffusion…"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Arrêter la diffusion de <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Si vous diffusez <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou que vous modifiez le résultat, votre annonce actuelle s\'arrêtera"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Diffuser <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Modifier le résultat"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Inconnue"</string> </resources> diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml index 47fa9c571402..8c6c4f555a5c 100644 --- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Désactivé"</item> <item msgid="460891964396502657">"Activé"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Indisponible"</item> + <item msgid="8014986104355098744">"Désactivé"</item> + <item msgid="5966994759929723339">"Activé"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 7204f72c20af..f83e181a622c 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Xirar automaticamente"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Xirar pantalla automaticamente"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localización"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Protector de pantalla"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Acceso á cámara"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Acceso ao micrófono"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dispoñible"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Queres continuar coa túa sesión?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Comezar de novo"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Si, continuar"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo de convidado"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Estás usando o modo de convidado"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ao engadir un usuario novo, sairás do modo de convidado e eliminaranse todas as aplicacións e datos da sesión de convidado actual."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Alcanzouse o límite de usuarios"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Podes engadir ata <xliff:g id="COUNT">%d</xliff:g> usuarios.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batería"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de pantalla"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mensaxes xerais"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Aplicacións Instantáneas"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configurar"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Almacenamento"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Consellos"</string> <string name="instant_apps" msgid="8337185853050247304">"Aplicacións Instantáneas"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarma definida"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A cámara e o micrófono están desactivados"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificación}other{# notificacións}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Difusión"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Queres deixar de emitir contido a través de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se emites contido a través de <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou cambias de saída, a emisión en curso deterase"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitir contido a través de <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambiar de saída"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Descoñecida"</string> </resources> diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml index 229836c76e09..590ec4ac515c 100644 --- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Desactivado"</item> <item msgid="460891964396502657">"Activado"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Non dispoñible"</item> + <item msgid="8014986104355098744">"Desactivado"</item> + <item msgid="5966994759929723339">"Activado"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index d28c47b58193..940f3c5734b4 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ઑટો રોટેટ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ઑટો રોટેટ સ્ક્રીન"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"સ્થાન"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"સ્ક્રીન સેવર"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"કૅમેરાનો ઍક્સેસ"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"માઇકનો ઍક્સેસ"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ઉપલબ્ધ છે"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"શું તમે તમારું સત્ર ચાલુ રાખવા માંગો છો?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"શરૂ કરો"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"હા, ચાલુ રાખો"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"અતિથિ મોડ"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"તમે અતિથિ મોડમાં છો"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"કોઈ નવા વપરાશકર્તાને ઉમેરવાથી અતિથિ મોડમાંથી નીકળી જવાશે તેમજ હાલના અતિથિ સત્રમાંથી તમામ ઍપ અને ડેટા ડિલીટ થઈ જશે."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"વપરાશકર્તા સંખ્યાની મર્યાદા"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">તમે <xliff:g id="COUNT">%d</xliff:g> વપરાશકર્તા સુધી ઉમેરી શકો છો.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"અલર્ટ"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"બૅટરી"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"સ્ક્રીનશૉટ"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"સામાન્ય સંદેશા"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"સેટઅપ કરો"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"સ્ટોરેજ"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"હિન્ટ"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"અલાર્મ સેટ"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"કૅમેરા અને માઇક બંધ છે"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# નોટિફિકેશન}one{# નોટિફિકેશન}other{# નોટિફિકેશન}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"બ્રૉડકાસ્ટ કરી રહ્યાં છે"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> બ્રોડકાસ્ટ કરવાનું રોકીએ?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"જો તમે <xliff:g id="SWITCHAPP">%1$s</xliff:g> બ્રોડકાસ્ટ કરો અથવા આઉટપુટ બદલો, તો તમારું હાલનું બ્રોડકાસ્ટ બંધ થઈ જશે"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> બ્રોડકાસ્ટ કરો"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"આઉટપુટ બદલો"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"અજાણ"</string> </resources> diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml index c502ba3e1ffa..73b372079ec9 100644 --- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"બંધ છે"</item> <item msgid="460891964396502657">"ચાલુ છે"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"અનુપલબ્ધ"</item> + <item msgid="8014986104355098744">"બંધ છે"</item> + <item msgid="5966994759929723339">"ચાલુ છે"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 2c5fec3af159..cc5f71aaa399 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रीन का अपने-आप दिशा बदलना (ऑटो-रोटेट)"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"जगह"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रीन सेवर"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"कैमरे का ऐक्सेस"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"माइक्रोफ़ोन का ऐक्सेस"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"क्या आपको अपना सेशन जारी रखना है?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"फिर से शुरू करें"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"हां, जारी रखें"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"मेहमान मोड"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"आप मेहमान मोड में हैं"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"किसी नए उपयोगकर्ता को जोड़ने पर, मेहमान मोड को बंद कर दिया जाएगा. साथ ही, मेहमान के तौर पर ब्राउज़ करने के मौजूदा सेशन से, सभी ऐप्लिकेशन और डेटा को मिटा दिया जाएगा."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"अब और उपयोगकर्ता नहीं जोड़े जा सकते"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">आप ज़्यादा से ज़्यादा <xliff:g id="COUNT">%d</xliff:g> उपयोगकर्ता जोड़ सकते हैं.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"सूचनाएं"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"बैटरी"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"स्क्रीनशॉट"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"सामान्य संदेश"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"सेट अप"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"स्टोरेज"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"संकेत"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"अलार्म सेट किया गया"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"कैमरा और माइक बंद हैं"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# सूचना}one{# सूचना}other{# सूचनाएं}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ब्रॉडकास्ट ऐप्लिकेशन"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर ब्रॉडकास्ट करना रोकें?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> पर ब्रॉडकास्ट शुरू करने पर या आउटपुट बदलने पर, आपका मौजूदा ब्रॉडकास्ट बंद हो जाएगा"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> पर ब्रॉडकास्ट करें"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"आउटपुट बदलें"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"कोई जानकारी नहीं"</string> </resources> diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml index 9af07bc65b8c..a156b0c43ca6 100644 --- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"बंद है"</item> <item msgid="460891964396502657">"चालू है"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"उपलब्ध नहीं है"</item> + <item msgid="8014986104355098744">"बंद है"</item> + <item msgid="5966994759929723339">"चालू है"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index a2c297af7324..3b4120e421de 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -227,6 +227,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. zakretanje"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko zakretanje zaslona"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Čuvar zaslona"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Pristup fotoaparatu"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Pristup mikrofonu"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Dostupno"</string> @@ -353,6 +354,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nastavi"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Način rada za goste"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Upotrebljavate način rada za goste"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ako dodate novog korisnika, napustit ćete način rada za goste i izbrisat će se svi podaci i aplikacije iz trenutačne gostujuće sesije."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Dosegnuto je ograničenje korisnika"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Možete dodati najviše <xliff:g id="COUNT">%d</xliff:g> korisnika.</item> @@ -702,7 +706,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Upozorenja"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Baterija"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Snimke zaslona"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Općenite poruke"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant aplikacije"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Postavljanje"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Pohrana"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Savjeti"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant aplikacije"</string> @@ -967,4 +972,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je postavljen"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotoaparat i mikrofon su isključeni"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obavijest}one{# obavijest}few{# obavijesti}other{# obavijesti}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Emitiranje"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zaustaviti emitiranje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ako emitirate aplikaciju <xliff:g id="SWITCHAPP">%1$s</xliff:g> ili promijenite izlaz, vaše će se trenutačno emitiranje zaustaviti"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Emitiranje aplikacije <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Promjena izlaza"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nepoznato"</string> </resources> diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml index a057c48bbec9..b69b06419281 100644 --- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Isključeno"</item> <item msgid="460891964396502657">"Uključeno"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Nedostupno"</item> + <item msgid="8014986104355098744">"Isključeno"</item> + <item msgid="5966994759929723339">"Uključeno"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 9f09ba7348c6..cf596c0c7ec7 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatikus elforgatás"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatikus képernyőforgatás"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyővédő"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Hozzáférés a kamerához"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonelérés"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Rendelkezésre áll"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Igen, folytatom"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Vendég mód"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Vendég módban van"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Új felhasználó felvételével kilép a vendég módból, és az aktuális vendégmunkamenetből származó összes alkalmazás és adat törlődik majd."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Maximális felhasználószám elérve"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Legfeljebb <xliff:g id="COUNT">%d</xliff:g> felhasználót adhat hozzá.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Értesítések"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Akkumulátor"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Képernyőképek"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Általános üzenetek"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Azonnali alkalmazások"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Beállítás"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Tárhely"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Tippek"</string> <string name="instant_apps" msgid="8337185853050247304">"Azonnali alkalmazások"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Ébresztő beállítva"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A kamera és a mikrofon ki vannak kapcsolva"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# értesítés}other{# értesítés}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Sugárzás"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Leállítja a(z) <xliff:g id="APP_NAME">%1$s</xliff:g> közvetítését?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"A(z) <xliff:g id="SWITCHAPP">%1$s</xliff:g> közvetítése vagy a kimenet módosítása esetén a jelenlegi közvetítés leáll"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> közvetítése"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Kimenet módosítása"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ismeretlen"</string> </resources> diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml index 47109a334e6d..050bc14d54ff 100644 --- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Ki"</item> <item msgid="460891964396502657">"Be"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Nem áll rendelkezésre"</item> + <item msgid="8014986104355098744">"Ki"</item> + <item msgid="5966994759929723339">"Be"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index fce482305558..49b2d0084286 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ինքնապտտում"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ավտոմատ պտտել էկրանը"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Տեղորոշում"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Էկրանապահ"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Տեսախցիկի հասանելիություն"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Խոսափողի հասանելիություն"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Հասանելի է"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Շարունակե՞լ աշխատաշրջանը։"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Վերսկսել"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Այո, շարունակել"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Հյուրի ռեժիմ"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Դուք հյուրի ռեժիմում եք"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ավելացնելով նոր օգտատեր՝ դուք դուրս կգաք հյուրի ռեժիմից։ Հյուրի ընթացիկ աշխատաշրջանի բոլոր հավելվածներն ու տվյալները կջնջվեն։"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Սահմանաչափը սպառված է"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Հնարավոր է ավելացնել առավելագույնը <xliff:g id="COUNT">%d</xliff:g> օգտատեր։</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Ծանուցումներ"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Մարտկոց"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Սքրինշոթներ"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Ընդհանուր հաղորդագրություններ"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Ակնթարթային հավելվածներ"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Կարգավորում"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Տարածք"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Հուշումներ"</string> <string name="instant_apps" msgid="8337185853050247304">"Ակնթարթային հավելվածներ"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Զարթուցիչը դրված է"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Տեսախցիկը և խոսափողն անջատված են"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ծանուցում}one{# ծանուցում}other{# ծանուցում}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Հեռարձակում"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Կանգնեցնել <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի հեռարձակումը"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Եթե հեռարձակեք <xliff:g id="SWITCHAPP">%1$s</xliff:g> հավելվածը կամ փոխեք աուդիո ելքը, ձեր ընթացիկ հեռարձակումը կկանգնեցվի։"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Հեռարձակել <xliff:g id="SWITCHAPP">%1$s</xliff:g> հավելվածը"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Փոխել աուդիո ելքը"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Անհայտ"</string> </resources> diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml index a78b7a10e58e..6015fbd75b3c 100644 --- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Անջատված է"</item> <item msgid="460891964396502657">"Միացված է"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Հասանելի չէ"</item> + <item msgid="8014986104355098744">"Անջատված է"</item> + <item msgid="5966994759929723339">"Միացված է"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index edcd553cb5ea..ffa26745e4af 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Putar Otomatis"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Putar layar otomatis"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Akses kamera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Akses mikrofon"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tersedia"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ya, lanjutkan"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Mode tamu"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Anda sedang menggunakan mode tamu"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Menambahkan pengguna baru akan membuat Anda keluar dari mode tamu dan menghapus semua aplikasi serta data dari sesi tamu saat ini."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Batas pengguna tercapai"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Anda dapat menambahkan hingga <xliff:g id="COUNT">%d</xliff:g> pengguna.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Notifikasi"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Baterai"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshot"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Pesan Umum"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Aplikasi Instan"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Penyiapan"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Penyimpanan"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Petunjuk"</string> <string name="instant_apps" msgid="8337185853050247304">"Aplikasi Instan"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm disetel"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera dan mikrofon nonaktif"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifikasi}other{# notifikasi}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Menyiarkan"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hentikan siaran <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jika Anda menyiarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g> atau mengubah output, siaran saat ini akan dihentikan"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ubah output"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tidak diketahui"</string> </resources> diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml index 6f81b29990cd..b8eb2f7d654e 100644 --- a/packages/SystemUI/res/values-in/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Nonaktif"</item> <item msgid="460891964396502657">"Aktif"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Tidak tersedia"</item> + <item msgid="8014986104355098744">"Nonaktif"</item> + <item msgid="5966994759929723339">"Aktif"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index f83e6047e4d0..f11cb5a69ea7 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Sjálfvirkur snúningur"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Snúa skjá sjálfkrafa"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Staðsetning"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Skjávari"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Aðgangur að myndavél"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Aðgangur að hljóðnema"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tiltækt"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Viltu halda áfram með lotuna?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Byrja upp á nýtt"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Já, halda áfram"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Gestastilling"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Þú ert í gestastillingu"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Þegar nýjum notanda er bætt við er gestastillingu lokað og öllum forritum og gögnum úr núverandi gestalotu eytt."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Notandahámarki náð"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Þú getur bætt við allt að <xliff:g id="COUNT">%d</xliff:g> notanda.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Tilkynningar"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Rafhlaða"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Skjámyndir"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Almenn skilaboð"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Skyndiforrit"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Uppsetning"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Geymslurými"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Vísbendingar"</string> <string name="instant_apps" msgid="8337185853050247304">"Skyndiforrit"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Vekjari stilltur"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Slökkt á myndavél og hljóðnema"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# tilkynning}one{# tilkynning}other{# tilkynningar}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Útsending í gangi"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hætta að senda út <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ef þú sendir út <xliff:g id="SWITCHAPP">%1$s</xliff:g> eða skiptir um úttak lýkur yfirstandandi útsendingu"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Senda út <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Skipta um úttak"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Óþekkt"</string> </resources> diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml index 29bce8257913..12dd776a357c 100644 --- a/packages/SystemUI/res/values-is/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Slökkt"</item> <item msgid="460891964396502657">"Kveikt"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Ekki í boði"</item> + <item msgid="8014986104355098744">"Slökkt"</item> + <item msgid="5966994759929723339">"Kveikt"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 2d25b9ba6de1..3bb37dc89565 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotazione automatica"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotazione automatica dello schermo"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Geolocalizzazione"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Salvaschermo"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Accesso alla fotocamera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Accesso al microfono"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponibile"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sì, continua"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modalità Ospite"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Sei in modalità Ospite"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Se aggiungi un nuovo utente, la modalità Ospite viene disattivata e vengono eliminati tutti i dati e le app della sessione Ospite corrente."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite di utenti raggiunto"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Puoi aggiungere fino a <xliff:g id="COUNT">%d</xliff:g> utenti.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Avvisi"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batteria"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshot"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Messaggi generali"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"App istantanee"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configurazione"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Spazio di archiviazione"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Suggerimenti"</string> <string name="instant_apps" msgid="8337185853050247304">"App istantanee"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Sveglia impostata"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotocamera e microfono non attivi"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notifica}other{# notifiche}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Trasmissione in corso…"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vuoi interrompere la trasmissione dell\'app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se trasmetti l\'app <xliff:g id="SWITCHAPP">%1$s</xliff:g> o cambi l\'uscita, la trasmissione attuale viene interrotta"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Trasmetti l\'app <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Cambia uscita"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Unknown"</string> </resources> diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml index 757a866463e6..5ec557bbaf7d 100644 --- a/packages/SystemUI/res/values-it/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Off"</item> <item msgid="460891964396502657">"On"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Non disponibile"</item> + <item msgid="8014986104355098744">"Off"</item> + <item msgid="5966994759929723339">"On"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index b42f28183420..f04106937ea3 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -228,6 +228,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"סיבוב אוטומטי"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"סיבוב אוטומטי של המסך"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"מיקום"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"שומר מסך"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"גישה למצלמה"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"גישה למיקרופון"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"יש גישה"</string> @@ -355,6 +356,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"האם ברצונך להמשיך בפעילות באתר?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"סשן חדש"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"כן, להמשיך"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"מצב אורח"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"התחברת במצב אורח"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"הוספת משתמש חדש תגרום ליציאה ממצב האורח ותמחק את כל האפליקציות והנתונים מהגלישה הנוכחית כאורח."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"הגעת למגבלת המשתמשים שניתן להוסיף"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="two">ניתן להוסיף עד <xliff:g id="COUNT">%d</xliff:g> משתמשים.</item> @@ -707,7 +711,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"התראות"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"סוללה"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"צילומי מסך"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"הודעות כלליות"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"אפליקציות ללא התקנה"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"הגדרה"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"אחסון"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"טיפים"</string> <string name="instant_apps" msgid="8337185853050247304">"אפליקציות ללא התקנה"</string> @@ -974,4 +979,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ההתראה מוגדרת"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"המצלמה והמיקרופון כבויים"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{התראה אחת}two{# התראות}many{# התראות}other{# התראות}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"שידור"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"האם להפסיק לשדר את התוכן מאפליקציית <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"אם משדרים את התוכן מאפליקציית <xliff:g id="SWITCHAPP">%1$s</xliff:g> או משנים את הפלט, השידור הנוכחי יפסיק לפעול"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"שידור תוכן מאפליקציית <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"שינוי הפלט"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"לא ידוע"</string> </resources> diff --git a/packages/SystemUI/res/values-iw/tiles_states_strings.xml b/packages/SystemUI/res/values-iw/tiles_states_strings.xml index 46be20c1f64d..91577b81a5d3 100644 --- a/packages/SystemUI/res/values-iw/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-iw/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"כבוי"</item> <item msgid="460891964396502657">"פועל"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"לא זמין"</item> + <item msgid="8014986104355098744">"כבוי"</item> + <item msgid="5966994759929723339">"מופעל"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 4159a37bcfd9..e80cb9cba74e 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動回転"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"画面を自動回転します"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"位置情報"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"スクリーン セーバー"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"カメラへのアクセス"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"マイクへのアクセス"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"使用可能"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"続行"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"ゲストモード"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"ゲストモード使用中"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"新しいユーザーを追加するとゲストモードは終了し、現在のゲスト セッションからすべてのアプリとデータが削除されます。"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"ユーザー数が上限に達しました"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">最大 <xliff:g id="COUNT">%d</xliff:g> 人のユーザーを追加できます。</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"アラート"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"バッテリー"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"スクリーンショット"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"一般メッセージ"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"セットアップ"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"ストレージ"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"ヒント"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"アラームを設定しました"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"カメラとマイクが OFF です"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 件の通知}other{# 件の通知}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ブロードキャスト"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> のブロードキャストを停止しますか?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> をブロードキャストしたり、出力を変更したりすると、現在のブロードキャストが停止します。"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> をブロードキャスト"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"出力を変更"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"不明"</string> </resources> diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml index fd966da986b8..c2a3321dc19f 100644 --- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"OFF"</item> <item msgid="460891964396502657">"ON"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"使用不可"</item> + <item msgid="8014986104355098744">"OFF"</item> + <item msgid="5966994759929723339">"ON"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 82f1c1f453cb..c91d867122fd 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ავტოროტაცია"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ეკრანის ავტომატური შეტრიალება"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"მდებარეობა"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ეკრანმზოგი"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"კამერაზე წვდომა"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"მიკროფონზე წვდომა"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ხელმისაწვდომი"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"გსურთ, თქვენი სესიის გაგრძელება?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ხელახლა დაწყება"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"დიახ, გავაგრძელოთ"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"სტუმრის რეჟიმი"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"თქვენ სტუმრის რეჟიმში ხართ"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"თუ ახალ მომხმარებელს დაამატებთ, სტუმრის რეჟიმი დაიხურება და სტუმრის რეჟიმის მიმდინარე სესიიდან ყველა აპი და მონაცემი წაიშლება."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"მიღწეულია მომხმარებელთა ლიმიტი"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">შესაძლებელია <xliff:g id="COUNT">%d</xliff:g>-მდე მომხმარებლის დამატება.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"გაფრთხილებები"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ბატარეა"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"ეკრანის ანაბეჭდები"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"ზოგადი შეტყობინებები"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"მყისიერი აპები"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"დაყენება"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"მეხსიერება"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"მინიშნებები"</string> <string name="instant_apps" msgid="8337185853050247304">"მყისიერი აპები"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"მაღვიძარა დაყენებულია"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"კამერა და მიკროფონი გამორთულია"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# შეტყობინება}other{# შეტყობინება}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"იწყებთ მაუწყებლობას"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"გსურთ <xliff:g id="APP_NAME">%1$s</xliff:g>-ის ტრანსლაციის შეჩერება?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>-ის ტრანსლაციის შემთხვევაში ან აუდიოს გამოსასვლელის შეცვლისას, მიმდინარე ტრანსლაცია შეჩერდება"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>-ის ტრანსლაცია"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"აუდიოს გამოსასვლელის შეცვლა"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"უცნობი"</string> </resources> diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml index 0c7d5af29890..c95187404d48 100644 --- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"გამორთვა"</item> <item msgid="460891964396502657">"ჩართვა"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"მიუწვდომელია"</item> + <item msgid="8014986104355098744">"გამორთულია"</item> + <item msgid="5966994759929723339">"ჩართულია"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index deab3cd2d990..57a6d178d11f 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматты түрде бұру"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматты айналатын экран"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Локация"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Скринсейвер"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Камераны пайдалану"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Микрофонды пайдалану"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Қолжетімді"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Иә, жалғастыру"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Қонақ режимі"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Сіз қонақ режиміндесіз"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Жаңа пайдаланушы қосылған кезде, қонақ режимі жабылады, ағымдағы қонақ сеансындағы барлық қолданба мен дерек жойылады."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Пайдаланушылар саны шегіне жетті"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> пайдаланушыға дейін енгізуге болады.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Ескертулер"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Батарея"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Скриншоттар"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Жалпы хабарлар"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Реттеу"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Жад"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Кеңестер"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Оятқыш орнатылды"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера мен микрофон өшірулі"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# хабарландыру}other{# хабарландыру}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Таратуда"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын таратуды тоқтатасыз ба?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> қолданбасын таратсаңыз немесе аудио шығысын өзгертсеңіз, қазіргі тарату сеансы тоқтайды."</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> қолданбасын тарату"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Аудио шығысын өзгерту"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Белгісіз"</string> </resources> diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml index 546666327e69..c312b4957615 100644 --- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Өшірулі"</item> <item msgid="460891964396502657">"Қосулы"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Қолжетімді емес."</item> + <item msgid="8014986104355098744">"Өшірулі."</item> + <item msgid="5966994759929723339">"Қосулы."</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index bb0a0b0b541a..1f723c52caa2 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"បង្វិលស្វ័យប្រវត្តិ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"បង្វិលអេក្រង់ស្វ័យប្រវត្តិ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ទីតាំង"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ធាតុរក្សាអេក្រង់"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"ការចូលប្រើកាមេរ៉ា"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"ការចូលប្រើមីក្រូហ្វូន"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"អាចចូលប្រើបាន"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"តើអ្នកចង់បន្តវគ្គរបស់អ្នកទេ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើមសាជាថ្មី"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"បាទ/ចាស បន្ត"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"មុខងារភ្ញៀវ"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"អ្នកស្ថិតនៅក្នុងមុខងារភ្ញៀវ"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ការបញ្ចូលអ្នកប្រើប្រាស់ថ្មីនឹងធ្វើឱ្យចាកចេញពីមុខងារភ្ញៀវ និងលុបកម្មវិធីនិងទិន្នន័យទាំងអស់ចេញពីវគ្គភ្ញៀវបច្ចុប្បន្ន។"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"បានឈានដល់ចំនួនកំណត់អ្នកប្រើប្រាស់"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">អ្នកអាចបញ្ចូលអ្នកប្រើប្រាស់បានរហូតដល់ <xliff:g id="COUNT">%d</xliff:g> នាក់។</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"ការជូនដំណឹង"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ថ្ម"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"រូបថតអេក្រង់"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"សារទូទៅ"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"កម្មវិធីប្រើភ្លាមៗ"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"ការរៀបចំ"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"ទំហំផ្ទុក"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"ការសម្រួល"</string> <string name="instant_apps" msgid="8337185853050247304">"កម្មវិធីប្រើភ្លាមៗ"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"រូបកំណត់ម៉ោងរោទ៍"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"កាមេរ៉ា និងមីក្រូហ្វូនត្រូវបានបិទ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{ការជូនដំណឹង #}other{ការជូនដំណឹង #}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ការផ្សាយ"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"បញ្ឈប់ការផ្សាយ <xliff:g id="APP_NAME">%1$s</xliff:g> ឬ?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ប្រសិនបើអ្នកផ្សាយ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ឬប្ដូរឧបករណ៍បញ្ចេញសំឡេង ការផ្សាយបច្ចុប្បន្នរបស់អ្នកនឹងបញ្ឈប់"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"ការផ្សាយ <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ប្ដូរឧបករណ៍បញ្ចេញសំឡេង"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"មិនស្គាល់"</string> </resources> diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml index f4830f5bbb75..ec748cfac142 100644 --- a/packages/SystemUI/res/values-km/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"បិទ"</item> <item msgid="460891964396502657">"បើក"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"មិនមានទេ"</item> + <item msgid="8014986104355098744">"បិទ"</item> + <item msgid="5966994759929723339">"បើក"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 917d16bd1752..55989b151ab0 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ಸ್ವಯಂ-ತಿರುಗುವಿಕೆ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ಪರದೆಯನ್ನು ಸ್ವಯಂ-ತಿರುಗಿಸಿ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ಸ್ಥಳ"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ಸ್ಕ್ರೀನ್ ಸೇವರ್"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"ಕ್ಯಾಮರಾ ಆ್ಯಕ್ಸೆಸ್"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"ಮೈಕ್ ಆ್ಯಕ್ಸೆಸ್"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ಲಭ್ಯವಿದೆ"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ನಿಮ್ಮ ಸೆಷನ್ ಮುಂದುವರಿಸಲು ಇಚ್ಚಿಸುವಿರಾ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ಪ್ರಾರಂಭಿಸಿ"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ಹೌದು, ಮುಂದುವರಿಸಿ"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"ಅತಿಥಿ ಮೋಡ್"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"ನೀವು ಅತಿಥಿ ಮೋಡ್ನಲ್ಲಿದ್ದೀರಿ"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸುವುದರಿಂದ ಅತಿಥಿ ಮೋಡ್ನಿಂದ ನಿರ್ಗಮಿಸುತ್ತದೆ ಮತ್ತು ಪ್ರಸ್ತುತ ಅತಿಥಿ ಸೆಶನ್ನಿಂದ ಎಲ್ಲಾ ಆ್ಯಪ್ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸುತ್ತದೆ."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"ಬಳಕೆದಾರರ ಮಿತಿ ತಲುಪಿದೆ"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">ನೀವು <xliff:g id="COUNT">%d</xliff:g> ಬಳಕೆದಾರರವರೆಗೆ ಸೇರಿಸಬಹುದು.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"ಅಲರ್ಟ್ಗಳು"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ಬ್ಯಾಟರಿ"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"ಸ್ಕ್ರೀನ್ಶಾಟ್ಗಳು"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"ಸಾಮಾನ್ಯ ಸಂದೇಶಗಳು"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"ಸೆಟಪ್"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"ಸಂಗ್ರಹಣೆ"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"ಸುಳಿವುಗಳು"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ಅಲಾರಾಂ ಹೊಂದಿಸಲಾಗಿದೆ"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ ಆಫ್ ಆಗಿದೆ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ಅಧಿಸೂಚನೆ}one{# ಅಧಿಸೂಚನೆಗಳು}other{# ಅಧಿಸೂಚನೆಗಳು}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ಪ್ರಸಾರ ಮಾಡಲಾಗುತ್ತಿದೆ"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ನ ಪ್ರಸಾರವನ್ನು ನಿಲ್ಲಿಸಬೇಕೆ?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ನೀವು <xliff:g id="SWITCHAPP">%1$s</xliff:g> ಅನ್ನು ಪ್ರಸಾರ ಮಾಡಿದರೆ ಅಥವಾ ಔಟ್ಪುಟ್ ಅನ್ನು ಬದಲಾಯಿಸಿದರೆ, ನಿಮ್ಮ ಪ್ರಸ್ತುತ ಪ್ರಸಾರವು ಸ್ಥಗಿತಗೊಳ್ಳುತ್ತದೆ"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ಅನ್ನು ಪ್ರಸಾರ ಮಾಡಿ"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ಔಟ್ಪುಟ್ ಅನ್ನು ಬದಲಾಯಿಸಿ"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ಅಪರಿಚಿತ"</string> </resources> diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml index 5cf2f5c2661e..864a607c8a39 100644 --- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"ಆಫ್ ಮಾಡಿ"</item> <item msgid="460891964396502657">"ಆನ್ ಮಾಡಿ"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"ಲಭ್ಯವಿಲ್ಲ"</item> + <item msgid="8014986104355098744">"ಆಫ್ ಮಾಡಿ"</item> + <item msgid="5966994759929723339">"ಆನ್ ಮಾಡಿ"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index a2d02d26168f..ad4ccdc09a53 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"자동 회전"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"화면 자동 회전"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"위치"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"화면 보호기"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"카메라 액세스"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"마이크 액세스"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"사용 가능"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"계속 진행"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"게스트 모드"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"게스트 모드 사용 중"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"새로운 사용자를 추가하면 게스트 모드가 종료되고 기존 게스트 세션의 모든 앱과 데이터가 삭제됩니다."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"사용자 제한 도달"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">사용자를 <xliff:g id="COUNT">%d</xliff:g>명까지 추가할 수 있습니다.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"알림"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"배터리"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"스크린샷"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"일반 메시지"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"인스턴트 앱"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"설정"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"저장용량"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"힌트"</string> <string name="instant_apps" msgid="8337185853050247304">"인스턴트 앱"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"알람이 설정되었습니다."</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"카메라 및 마이크가 사용 중지되었습니다."</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{알림 #개}other{알림 #개}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"방송 중"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> 방송을 중지하시겠습니까?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> 앱을 방송하거나 출력을 변경하면 기존 방송이 중단됩니다"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> 방송"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"출력 변경"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"알 수 없음"</string> </resources> diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml index 3244ffaec49c..c52c17cedc32 100644 --- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"꺼짐"</item> <item msgid="460891964396502657">"켜짐"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"사용 불가"</item> + <item msgid="8014986104355098744">"꺼짐"</item> + <item msgid="5966994759929723339">"켜짐"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 383df9036fa0..cc70d00476ac 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Авто буруу"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Экранды авто буруу"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Жайгашкан жер"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Көшөгө"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Камера"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Микрофон"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Жеткиликтүү"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ооба, уланта берели"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Конок режими"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Конок режиминдесиз"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Жаңы колдонуучуну кошсоңуз, конок режими жабылып, учурдагы конок сеансындагы бардык колдонмолор жана башка нерселер өчүп калат."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Дагы колдонуучу кошууга болбойт"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> колдонуучуга чейин кошууга болот.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Эскертүүлөр"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Батарея"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Скриншоттор"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Жалпы билдирүүлөр"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Ыкчам ачылуучу колдонмолор"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Тууралоо"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Сактагыч"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Кеңештер"</string> <string name="instant_apps" msgid="8337185853050247304">"Ыкчам ачылуучу колдонмолор"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Ойготкуч коюлду"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера жана микрофон өчүк"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# билдирме}other{# билдирме}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Кеңири таратуу"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунда кабарлоо токтотулсунбу?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Эгер <xliff:g id="SWITCHAPP">%1$s</xliff:g> колдонмосунда кабарласаңыз же аудионун чыгуусун өзгөртсөңүз, учурдагы кабарлоо токтотулат"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> колдонмосунда кабарлоо"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Аудионун чыгуусун өзгөртүү"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Белгисиз"</string> </resources> diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml index 5518fccac6ee..f872926aa945 100644 --- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Өчүк"</item> <item msgid="460891964396502657">"Күйүк"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Жеткиликсиз"</item> + <item msgid="8014986104355098744">"Өчүк"</item> + <item msgid="5966994759929723339">"Күйүк"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index cc44b5984987..e2ecaaa2b17f 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ໝຸນອັດຕະໂນມັດ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ໝຸນໜ້າຈໍອັດຕະໂນມັດ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ສະຖານທີ່"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ພາບພັກໜ້າຈໍ"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"ການເຂົ້າເຖິງກ້ອງຖ່າຍຮູບ"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"ການເຂົ້າເຖິງໄມ"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ສາມາດໃຊ້ໄດ້"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານຕ້ອງການສືບຕໍ່ເຊດຊັນຂອງທ່ານບໍ່?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ຕົກລົງ, ດຳເນີນການຕໍ່"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"ໂໝດແຂກ"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"ທ່ານກຳລັງຢູ່ໃນໂໝດແຂກ"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ການເພີ່ມຜູ້ໃຊ້ໃໝ່ຈະອອກຈາກໂໝດແຂກ ແລະ ລຶບແອັບ ແລະ ຂໍ້ມູນທັງໝົດອອກຈາກເຊດຊັນແຂກປັດຈຸບັນ."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"ຮອດຂີດຈຳກັດຜູ້ໃຊ້ແລ້ວ"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">ທ່ານສາມາດເພີ່ມໄດ້ສູງສຸດ <xliff:g id="COUNT">%d</xliff:g> ຄົນ.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"ການແຈ້ງເຕືອນ"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ແບັດເຕີຣີ"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"ຮູບຖ່າຍໜ້າຈໍ"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"ຂໍ້ຄວາມທົ່ວໄປ"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"ອິນສະແຕນແອັບ"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"ຕັ້ງຄ່າ"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"ບ່ອນເກັບຂໍ້ມູນ"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"ຄຳໃບ້"</string> <string name="instant_apps" msgid="8337185853050247304">"ອິນສະແຕນແອັບ"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ຕັ້ງໂມງປຸກແລ້ວ"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ປິດກ້ອງຖ່າຍຮູບ ແລະ ໄມແລ້ວ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ການແຈ້ງເຕືອນ}other{# ການແຈ້ງເຕືອນ}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ກຳລັງອອກອາກາດ"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"ຢຸດການອອກອາກາດ <xliff:g id="APP_NAME">%1$s</xliff:g> ບໍ?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ຫາກທ່ານອອກອາກາດ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ຫຼື ປ່ຽນເອົ້າພຸດ, ການອອກອາກາດປັດຈຸບັນຂອງທ່ານຈະຢຸດ"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"ອອກອາກາດ <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ປ່ຽນເອົ້າພຸດ"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ບໍ່ຮູ້ຈັກ"</string> </resources> diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml index c6b7e6c6e74d..6ae37e472b0c 100644 --- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"ປິດ"</item> <item msgid="460891964396502657">"ເປີດ"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"ບໍ່ສາມາດໃຊ້ໄດ້"</item> + <item msgid="8014986104355098744">"ປິດ"</item> + <item msgid="5966994759929723339">"ເປີດ"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index a4e21b62a3b4..effd57278432 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -228,6 +228,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatinis pasukimas"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatiškai sukti ekraną"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Vietovė"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekrano užsklanda"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Prieiga prie fotoaparato"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Prieiga prie mikrofono"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Pasiekiama"</string> @@ -355,6 +356,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ar norite tęsti sesiją?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Pradėti iš naujo"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Taip, tęsti"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Svečio režimas"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Naudojatės svečio režimu"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Pridėjus naują naudotoją, bus išeita iš svečio režimo ir iš dabartinės svečio sesijos bus ištrintos visos programos ir duomenys."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Pasiekta naudotojų riba"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Galite pridėti iki <xliff:g id="COUNT">%d</xliff:g> naudotojo.</item> @@ -707,7 +711,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Įspėjimai"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Akumuliatorius"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Ekrano kopijos"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Bendrieji pranešimai"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Akimirksniu įkeliamos programos"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Sąranka"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Saugykla"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Užuominos"</string> <string name="instant_apps" msgid="8337185853050247304">"Akimirksniu įkeliamos programos"</string> @@ -974,4 +979,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Signalas nustatytas"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Vaizdo kamera ir mikrofonas išjungti"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# pranešimas}one{# pranešimas}few{# pranešimai}many{# pranešimo}other{# pranešimų}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transliavimas"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Sustabdyti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ transliaciją?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jei transliuosite „<xliff:g id="SWITCHAPP">%1$s</xliff:g>“ arba pakeisite išvestį, dabartinė transliacija bus sustabdyta"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transliuoti „<xliff:g id="SWITCHAPP">%1$s</xliff:g>“"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Keisti išvestį"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nežinoma"</string> </resources> diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml index 3e289e1f21ce..03d98c42ff19 100644 --- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Išjungta"</item> <item msgid="460891964396502657">"Įjungta"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Nepasiekiama"</item> + <item msgid="8014986104355098744">"Išjungta"</item> + <item msgid="5966994759929723339">"Įjungta"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 0506c8433536..aa4aa7f43e6b 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -227,6 +227,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automātiska pagriešana"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automātiska ekrāna pagriešana"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Atrašanās vieta"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekrānsaudzētājs"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kamera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofons"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Piekļuve atļauta"</string> @@ -353,6 +354,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vai vēlaties turpināt savu sesiju?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Sākt no sākuma"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Jā, turpināt"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Viesa režīms"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Jūs izmantojat viesa režīmu"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ja pievienosiet jaunu lietotāju, viesa režīms tiks aizvērts un visas pašreizējās viesa sesijas lietotnes un dati tiks dzēsti."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Sasniegts lietotāju ierobežojums"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="zero">Varat pievienot ne vairāk kā <xliff:g id="COUNT">%d</xliff:g> lietotājus.</item> @@ -702,7 +706,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Brīdinājumi"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Akumulators"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Ekrānuzņēmumi"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Vispārīgi ziņojumi"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Tūlītējās lietotnes"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Iestatīšana"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Krātuve"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Padomi"</string> <string name="instant_apps" msgid="8337185853050247304">"Tūlītējās lietotnes"</string> @@ -967,4 +972,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Signāls ir iestatīts"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera un mikrofons ir izslēgti"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# paziņojums}zero{# paziņojumu}one{# paziņojums}other{# paziņojumi}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Notiek apraidīšana"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vai apturēt lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> apraidīšanu?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ja sāksiet lietotnes <xliff:g id="SWITCHAPP">%1$s</xliff:g> apraidīšanu vai mainīsiet izvadi, pašreizējā apraide tiks apturēta"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Lietotnes <xliff:g id="SWITCHAPP">%1$s</xliff:g> apraide"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Izvades maiņa"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Nezināms"</string> </resources> diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml index eb210c24bb50..6e9264d2a347 100644 --- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Izslēgts"</item> <item msgid="460891964396502657">"Ieslēgts"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Nav pieejams"</item> + <item msgid="8014986104355098744">"Izslēgts"</item> + <item msgid="5966994759929723339">"Ieslēgts"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 9e09630ef4b1..8049bd4ae1d0 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматско ротирање"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматско ротирање на екранот"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Заштитник на екран"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Пристап до камерата"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Пристап до микрофонот"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Дозволен"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, продолжи"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Режим на гостин"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Користите режим на гостин"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ако додадете нов корисник, ќе излезете од режимот на гостин и ќе ги избришете сите апликации и податоци од тековната гостинска сесија."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнато ограничување на корисник"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Може да додадете најмногу <xliff:g id="COUNT">%d</xliff:g> корисник.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Известувања"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Батерија"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Слики од екранот"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Општи пораки"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Инстант апликации"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Поставување"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Капацитет"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Совети"</string> <string name="instant_apps" msgid="8337185853050247304">"Инстант апликации"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Алармот е наместен"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камерата и микрофонот се исклучени"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# известување}one{# известување}other{# известувања}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Емитување"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Да се прекине емитувањето на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако емитувате на <xliff:g id="SWITCHAPP">%1$s</xliff:g> или го промените излезот, тековното емитување ќе запре"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Емитување на <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Променете излез"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Непознато"</string> </resources> diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml index 78be6dda9184..96c8a49ae0b6 100644 --- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Исклучен"</item> <item msgid="460891964396502657">"Вклучен"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Недостапно"</item> + <item msgid="8014986104355098744">"Исклучено"</item> + <item msgid="5966994759929723339">"Вклучено"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 6a030a699ba5..ebb9cbe2e0bf 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"സ്ക്രീൻ സ്വയമേവ തിരിയൽ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"സ്ക്രീൻ സ്വയമേവ തിരിക്കുക"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ലൊക്കേഷൻ"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"സ്ക്രീൻ സേവർ"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"ക്യാമറ ആക്സസ്"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"മൈക്ക് ആക്സസ്"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ലഭ്യമാണ്"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"നിങ്ങളുടെ സെഷൻ തുടരണോ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"പുനരാംരംഭിക്കുക"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"അതെ, തുടരുക"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"അതിഥി മോഡ്"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"നിങ്ങൾ അതിഥി മോഡിലാണ്"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"പുതിയൊരു ഉപയോക്താവിനെ ചേർത്താൽ അതിഥി മോഡിൽ നിന്ന് പുറത്ത് കടക്കുകയും നിലവിലെ അതിഥി മോഡിലുള്ള എല്ലാ ആപ്പുകളും ഡാറ്റയും ഇല്ലാതാക്കുകയും ചെയ്യും."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"ഉപയോക്തൃ പരിധി എത്തി"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">നിങ്ങൾക്ക് <xliff:g id="COUNT">%d</xliff:g> ഉപയോക്താക്കളെ വരെ ചേർക്കാനാവും.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"മുന്നറിയിപ്പുകൾ"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ബാറ്ററി"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"സ്ക്രീൻഷോട്ടുകൾ"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"പൊതുവായ സന്ദേശങ്ങൾ"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"സജ്ജീകരണം"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"സ്റ്റോറേജ്"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"സൂചനകൾ"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"അലാറം സജ്ജീകരിച്ചു"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ക്യാമറയും മൈക്കും ഓഫാണ്"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# അറിയിപ്പ്}other{# അറിയിപ്പുകൾ}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"പ്രക്ഷേപണം ചെയ്യുന്നു"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ബ്രോഡ്കാസ്റ്റ് ചെയ്യുന്നത് അവസാനിപ്പിക്കണോ?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"നിങ്ങൾ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ബ്രോഡ്കാസ്റ്റ് ചെയ്യുകയോ ഔട്ട്പുട്ട് മാറ്റുകയോ ചെയ്താൽ നിങ്ങളുടെ നിലവിലുള്ള ബ്രോഡ്കാസ്റ്റ് അവസാനിക്കും"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ബ്രോഡ്കാസ്റ്റ് ചെയ്യുക"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ഔട്ട്പുട്ട് മാറ്റുക"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"അജ്ഞാതം"</string> </resources> diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml index 12089288d134..7a078737aa96 100644 --- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"ഓഫാണ്"</item> <item msgid="460891964396502657">"ഓണാണ്"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"ലഭ്യമല്ല"</item> + <item msgid="8014986104355098744">"ഓഫാണ്"</item> + <item msgid="5966994759929723339">"ഓണാണ്"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index fbf90e444700..1da11746473c 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматаар эргэх"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Дэлгэцийг автоматаар эргүүлэх"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Байршил"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Дэлгэц амраагч"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Камерын хандалт"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Микрофоны хандалт"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Боломжтой"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Тийм, үргэлжлүүлэх"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Зочны горим"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Та зочны горимд байна"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Шинэ хэрэглэгч нэмснээр зочны горимоос гаргах бөгөөд бүх апп болон өгөгдлийг одоогийн зочны харилцан үйлдлээс устгана."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Хэрэглэгчийн хязгаарт хүрсэн"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Та <xliff:g id="COUNT">%d</xliff:g> хүртэлх хэрэглэгч нэмэх боломжтой.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Сэрэмжлүүлэг"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Батарей"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Дэлгэцийн зураг дарах"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Энгийн мессеж"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Шуурхай апп"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Тохируулга"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Хадгалах сан"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Заавар"</string> <string name="instant_apps" msgid="8337185853050247304">"Шуурхай апп"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Сэрүүлгийг тохируулсан"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камер болон микрофон унтраалттай байна"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# мэдэгдэл}other{# мэдэгдэл}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Нэвтрүүлэлт"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г нэвтрүүлэхээ зогсоох уу?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Хэрэв та <xliff:g id="SWITCHAPP">%1$s</xliff:g>-г нэвтрүүлсэн эсвэл гаралтыг өөрчилсөн бол таны одоогийн нэвтрүүлэлтийг зогсооно"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>-г нэвтрүүлэх"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Гаралтыг өөрчлөх"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Тодорхойгүй"</string> </resources> diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml index 3748440058b6..776c4877d1ad 100644 --- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Унтраалттай"</item> <item msgid="460891964396502657">"Асаалттай"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Боломжгүй"</item> + <item msgid="8014986104355098744">"Унтраалттай"</item> + <item msgid="5966994759929723339">"Асаалттай"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 316c9443980f..dfed3e39a49d 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ऑटो-रोटेट"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ऑटो-रोटेट स्क्रीन"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"स्थान"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रीन सेव्हर"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"कॅमेराचा अॅक्सेस"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"माइकचा ॲक्सेस"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध आहे"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"होय, सुरू ठेवा"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"अतिथी मोड"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"तुम्ही अतिथी मोडमध्ये आहात"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"नवीन वापरकर्ता जोडल्याने अतिथी मोडमधून बाहेर पडाल आणि सध्याच्या अतिथी सत्रातील सर्व अॅप्स व डेटा हटवला जाईल."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"वापरकर्ता मर्यादा गाठली"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">तुम्ही <xliff:g id="COUNT">%d</xliff:g> वापरकर्त्यांपर्यंत जोडू शकता.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"सूचना"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"बॅटरी"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"स्क्रीनशॉट"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"सर्वसाधारण मेसेज"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"सेटअप"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"स्टोरेज"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"सूचना"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"अलार्म सेट केला"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"कॅमेरा आणि माइक बंद आहेत"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# सूचना}other{# सूचना}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ब्रॉडकास्ट करत आहे"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> चे प्रसारण थांबवायचे आहे का?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"तुम्ही <xliff:g id="SWITCHAPP">%1$s</xliff:g> चे प्रसारण केल्यास किंवा आउटपुट बदलल्यास, तुमचे सध्याचे प्रसारण बंद होईल"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> चे प्रसारण करा"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"आउटपूट बदला"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"अज्ञात"</string> </resources> diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml index a6a3873f95d7..f75f0d097998 100644 --- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"बंद आहे"</item> <item msgid="460891964396502657">"सुरू आहे"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"उपलब्ध नाही"</item> + <item msgid="8014986104355098744">"बंद आहे"</item> + <item msgid="5966994759929723339">"सुरू आहे"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index fac7f39f7256..c0ca50533c8b 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoputar"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoputar skrin"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasi"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Penyelamat skrin"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Akses kamera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Akses mikrofon"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tersedia"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Adakah anda ingin meneruskan sesi anda?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulakan semula"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ya, teruskan"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Mod tetamu"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Anda dalam mod tetamu"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Tindakan menambahkan pengguna baharu akan menyebabkan anda keluar daripada mod tetamu dan memadamkan semua apl dan data daripada sesi tetamu semasa."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Had pengguna dicapai"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Anda boleh menambah sehingga <xliff:g id="COUNT">%d</xliff:g> pengguna.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Makluman"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Bateri"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Tangkapan skrin"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mesej Am"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Apl Segera"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Persediaan"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Storan"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Pembayang"</string> <string name="instant_apps" msgid="8337185853050247304">"Apl Segera"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Penggera ditetapkan"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera dan mikrofon dimatikan"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# pemberitahuan}other{# pemberitahuan}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Menyiarkan"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Hentikan siaran <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jika anda siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g> atau tukarkan output, siaran semasa anda akan berhenti"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Siarkan <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Tukar output"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Tidak diketahui"</string> </resources> diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml index cfd831a1d94b..9fa7ab51d064 100644 --- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Mati"</item> <item msgid="460891964396502657">"Hidup"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Tidak tersedia"</item> + <item msgid="8014986104355098744">"Mati"</item> + <item msgid="5966994759929723339">"Hidup"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index a76b792b615a..2611f9bcd002 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"အော်တို-လည်"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"မျက်နှာပြင်အား အလိုအလျောက်လှည့်ခြင်း"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"တည်နေရာ"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"စကရင်ချွေတာစနစ်"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"ကင်မရာသုံးခွင့်"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"မိုက်သုံးခွင့်"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ရနိုင်သည်"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်၏ စက်ရှင်ကို ဆက်လုပ်လိုပါသလား။"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ပြန်စပါ"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ဆက်လုပ်ပါ"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"ဧည့်သည်မုဒ်"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"သင်သည် ဧည့်သည်မုဒ်တွင် ဖြစ်သည်"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"အသုံးပြုသူ အသစ်ထည့်ခြင်းက ဧည့်သည်မုဒ်မှ ထွက်သွားမည်ဖြစ်ပြီး လက်ရှိဧည့်သည် စက်ရှင်မှ အက်ပ်နှင့် ဒေတာအားလုံးကို ဖျက်လိုက်ပါမည်။"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"အသုံးပြုသူ အကန့်အသတ် ပြည့်သွားပါပြီ"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">အသုံးပြုသူ <xliff:g id="COUNT">%d</xliff:g> ဦးအထိ ထည့်နိုင်သည်။</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"သတိပေးချက်များ"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ဘက်ထရီ"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"မျက်နှာပြင်ဓာတ်ပုံများ"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"အထွေထွေ မက်ဆေ့ဂျ်များ"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"စနစ်ထည့်ရန်"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"သိုလှောင်ခန်း"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"အရိပ်အမြွက်များ"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"နိုးစက် သတ်မှတ်ထားသည်"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ကင်မရာနှင့် မိုက် ပိတ်ထားသည်"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{အကြောင်းကြားချက် # ခု}other{အကြောင်းကြားချက် # ခု}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ထုတ်လွှင့်ခြင်း"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ထုတ်လွှင့်ခြင်းကို ရပ်မလား။"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ကို ထုတ်လွှင့်သောအခါ (သို့) အထွက်ကို ပြောင်းသောအခါ သင့်လက်ရှိထုတ်လွှင့်ခြင်း ရပ်သွားမည်"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ထုတ်လွှင့်ခြင်း"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"အထွက်ကို ပြောင်းခြင်း"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"မသိ"</string> </resources> diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml index 665af20761c7..493a7f0977c6 100644 --- a/packages/SystemUI/res/values-my/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"ပိတ်"</item> <item msgid="460891964396502657">"ဖွင့်"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"မရနိုင်ပါ"</item> + <item msgid="8014986104355098744">"ပိတ်"</item> + <item msgid="5966994759929723339">"ဖွင့်"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 57be1d7de0c0..49d31101a379 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotér automatisk"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotér skjermen automatisk"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Sted"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Skjermsparer"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameratilgang"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofontilgang"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tilgjengelig"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsette økten?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start på nytt"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, fortsett"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Gjestemodus"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Du er i gjestemodus"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Hvis du legger til en ny bruker, avsluttes gjestemodus, og alle apper og data fra den gjeldende gjesteøkten slettes."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Grensen for antall brukere er nådd"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Du kan legge til opptil <xliff:g id="COUNT">%d</xliff:g> brukere.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Varsler"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batteri"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Skjermdumper"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Generelle meldinger"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Konfigurering"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Lagring"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Hint"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmen er stilt inn"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera og mikrofon er av"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# varsel}other{# varsler}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Kringkaster"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vil du stoppe kringkastingen av <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Hvis du kringkaster <xliff:g id="SWITCHAPP">%1$s</xliff:g> eller endrer utgangen, stopper den nåværende kringkastingen din"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Kringkast <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Endre utgang"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Ukjent"</string> </resources> diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml index a28b817b765b..6fa902a662ff 100644 --- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Av"</item> <item msgid="460891964396502657">"På"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Utilgjengelig"</item> + <item msgid="8014986104355098744">"Av"</item> + <item msgid="5966994759929723339">"På"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 31b4fb4a1e31..71533696412a 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"अटो रोटेट"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"स्क्रिन स्वतःघुम्ने"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"लोकेसन"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"स्क्रिन सेभर"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"क्यामेरा प्रयोग गर्ने अनुमति"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"माइक प्रयोग गर्ने अनुमति"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"उपलब्ध छ"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"तपाईं आफ्नो सत्र जारी गर्न चाहनुहुन्छ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"सुरु गर्नुहोस्"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"हो, जारी राख्नुहोस्"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"अतिथि मोड"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"तपाईं अतिथि मोड चलाउँदै हुनुहुन्छ"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"तपाईंले नयाँ प्रयोगकर्ता थप्नुभयो भने तपाईं अतिथि मोडबाट बाहिरिनु हुने छ र हालको अतिथि सत्रका सबै एप तथा डेटा मेटिने छ।"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"प्रयोगकर्ताको सीमा पुग्यो"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">तपाईं अधिकतम <xliff:g id="COUNT">%d</xliff:g> प्रयोगहरू मात्र थप्न सक्नुहुन्छ।</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"सतर्कताहरू"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ब्याट्री"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"स्क्रिनशटहरू"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"सामान्य सन्देशहरू"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"सेटअप गर्नुहोस्"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"भण्डारण"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"सङ्केतहरू"</string> <string name="instant_apps" msgid="8337185853050247304">"तात्कालिक एपहरू"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"अलार्म सेट गरिएको छ"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"क्यामेरा र माइक अफ छन्"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# वटा सूचना}other{# वटा सूचनाहरू}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"प्रसारण गरिँदै छ"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ब्रोडकास्ट गर्न छाड्ने हो?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"तपाईंले <xliff:g id="SWITCHAPP">%1$s</xliff:g> ब्रोडकास्ट गर्नुभयो वा आउटपुट परिवर्तन गर्नुभयो भने तपाईंको हालको ब्रोडकास्ट रोकिने छ"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ब्रोडकास्ट गर्नुहोस्"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"आउटपुट परिवर्तन गर्नुहोस्"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"अज्ञात"</string> </resources> diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml index 80edf29f4985..17193bafd9a3 100644 --- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"अफ छ"</item> <item msgid="460891964396502657">"अन छ"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"अनुपलब्ध"</item> + <item msgid="8014986104355098744">"अफ"</item> + <item msgid="5966994759929723339">"अन"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 85d7f97eb32f..09d4db4b814a 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatisch draaien"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Scherm automatisch draaien"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Locatie"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Cameratoegang"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Microfoontoegang"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Beschikbaar"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, doorgaan"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Gastmodus"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Je gebruikt de gastmodus"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Als je een nieuwe gebruiker toevoegt, wordt de gastmodus afgesloten en worden alle apps en gegevens van de huidige gastsessie verwijderd."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Gebruikerslimiet bereikt"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Je kunt maximaal <xliff:g id="COUNT">%d</xliff:g> gebruikers toevoegen.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Meldingen"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batterij"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Screenshots"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Algemene berichten"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant-apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Instellen"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Opslag"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Hints"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant-apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Wekker gezet"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera en microfoon staan uit"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# melding}other{# meldingen}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Uitzending"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Uitzending van <xliff:g id="APP_NAME">%1$s</xliff:g> stopzetten?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Als je <xliff:g id="SWITCHAPP">%1$s</xliff:g> uitzendt of de uitvoer wijzigt, wordt je huidige uitzending gestopt"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uitzenden"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Uitvoer wijzigen"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Onbekend"</string> </resources> diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml index c4603b22157c..fbccd78eeb8f 100644 --- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Uit"</item> <item msgid="460891964396502657">"Aan"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Niet beschikbaar"</item> + <item msgid="8014986104355098744">"Uit"</item> + <item msgid="5966994759929723339">"Aan"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 2df184136899..0d6253dc3785 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ଅଟୋ-ରୋଟେଟ୍"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ଅଟୋ-ରୋଟେଟ୍ ସ୍କ୍ରିନ୍"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ଲୋକେସନ୍"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ସ୍କ୍ରିନ ସେଭର"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"କ୍ୟାମେରା ଆକ୍ସେସ୍"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"ମାଇକ୍ ଆକ୍ସେସ୍"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ଉପଲବ୍ଧ"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ସେସନ୍ ଜାରି ରଖିବାକୁ ଚାହାଁନ୍ତି କି?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ହଁ, ଜାରି ରଖନ୍ତୁ"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"ଅତିଥି ମୋଡ"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"ଆପଣ ଅତିଥି ମୋଡରେ ଅଛନ୍ତି"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ଜଣେ ନୂଆ ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରିବା ଦ୍ୱାରା ଅତିଥି ମୋଡରୁ ବାହାରି ଯିବ ଏବଂ ବର୍ତ୍ତମାନର ଅତିଥି ସେସନରୁ ସମସ୍ତ ଆପ ଓ ଡାଟା ଡିଲିଟ ହୋଇଯିବ।"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"ଉପଯୋଗକର୍ତ୍ତା ସୀମାରେ ପହଞ୍ଚିଛି"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">କେବଳ <xliff:g id="COUNT">%d</xliff:g> ଉପଯୋଗକର୍ତ୍ତା ହିଁ ତିଆରି କରିହେବ।</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"ଆଲର୍ଟଗୁଡ଼ିକ"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ବ୍ୟାଟେରୀ"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"ସ୍କ୍ରୀନଶଟ୍"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"ସାଧାରଣ ମେସେଜ୍"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"ସେଟଅପ"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"ଷ୍ଟୋରେଜ୍"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"ହିଣ୍ଟ"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ଆଲାରାମ ସେଟ"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"କ୍ୟାମେରା ଏବଂ ମାଇକ ବନ୍ଦ ଅଛି"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{#ଟି ବିଜ୍ଞପ୍ତି}other{#ଟି ବିଜ୍ଞପ୍ତି}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ବ୍ରଡକାଷ୍ଟ କରୁଛି"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରିବା ବନ୍ଦ କରିବେ?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ଯଦି ଆପଣ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରନ୍ତି କିମ୍ବା ଆଉଟପୁଟ ବଦଳାନ୍ତି, ତେବେ ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ବ୍ରଡକାଷ୍ଟ ବନ୍ଦ ହୋଇଯିବ"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ବ୍ରଡକାଷ୍ଟ କରନ୍ତୁ"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ଆଉଟପୁଟ ବଦଳାନ୍ତୁ"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ଅଜଣା"</string> </resources> diff --git a/packages/SystemUI/res/values-or/tiles_states_strings.xml b/packages/SystemUI/res/values-or/tiles_states_strings.xml index 2d9fb84790cd..acaa3fb6f6a8 100644 --- a/packages/SystemUI/res/values-or/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-or/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"ବନ୍ଦ ଅଛି"</item> <item msgid="460891964396502657">"ଚାଲୁ ଅଛି"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"ଉପଲବ୍ଧ ନାହିଁ"</item> + <item msgid="8014986104355098744">"ବନ୍ଦ ଅଛି"</item> + <item msgid="5966994759929723339">"ଚାଲୁ ଅଛି"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 7a6e29b2f69e..207a318b7bad 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ਸਵੈ-ਘੁਮਾਓ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ਸਕ੍ਰੀਨ ਨੂੰ ਆਪਣੇ ਆਪ ਘੁੰਮਾਓ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ਟਿਕਾਣਾ"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ਸਕ੍ਰੀਨ ਸੇਵਰ"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"ਕੈਮਰਾ ਪਹੁੰਚ"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"ਮਾਈਕ ਪਹੁੰਚ"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"ਉਪਲਬਧ"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਮੁੜ-ਸ਼ੁਰੂ ਕਰੋ"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ਹਾਂ, ਜਾਰੀ ਰੱਖੋ"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"ਮਹਿਮਾਨ ਮੋਡ"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"ਤੁਸੀਂ ਮਹਿਮਾਨ ਮੋਡ ਵਿੱਚ ਹੋ"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"ਨਵੇਂ ਵਰਤੋਂਕਾਰ ਨੂੰ ਸ਼ਾਮਲ ਕਰਨ ਨਾਲ ਮੌਜੂਦਾ ਮਹਿਮਾਨ ਮੋਡ ਚਲਾ ਜਾਵੇਗਾ ਅਤੇ ਮੌਜੂਦਾ ਮਹਿਮਾਨ ਸੈਸ਼ਨ ਦੀਆਂ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਮਿਟ ਜਾਵੇਗਾ।"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰਨ ਦੀ ਸੀਮਾ ਪੂਰੀ ਹੋਈ"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">ਤੁਸੀਂ <xliff:g id="COUNT">%d</xliff:g> ਤੱਕ ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰ ਸਕਦੇ ਹੋ।</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"ਸੁਚੇਤਨਾਵਾਂ"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"ਬੈਟਰੀ"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"ਸਕ੍ਰੀਨਸ਼ਾਟ"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"ਆਮ ਸੁਨੇਹੇ"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"ਸੈੱਟਅੱਪ ਕਰੋ"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"ਸਟੋਰੇਜ"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"ਸੰਕੇਤ"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ਅਲਾਰਮ ਸੈੱਟ ਹੈ"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"ਕੈਮਰਾ ਅਤੇ ਮਾਈਕ ਬੰਦ ਹਨ"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ਸੂਚਨਾ}one{# ਸੂਚਨਾ}other{# ਸੂਚਨਾਵਾਂ}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ਪ੍ਰਸਾਰਨ"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਦੇ ਪ੍ਰਸਾਰਨ ਨੂੰ ਰੋਕਣਾ ਹੈ?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ਜੇ ਤੁਸੀਂ <xliff:g id="SWITCHAPP">%1$s</xliff:g> ਦਾ ਪ੍ਰਸਾਰਨ ਕਰਦੇ ਹੋ ਜਾਂ ਆਊਟਪੁੱਟ ਬਦਲਦੇ ਹੋ, ਤਾਂ ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਪ੍ਰਸਾਰਨ ਰੁਕ ਜਾਵੇਗਾ"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ਦਾ ਪ੍ਰਸਾਰਨ ਕਰੋ"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ਆਊਟਪੁੱਟ ਬਦਲੋ"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ਅਗਿਆਤ"</string> </resources> diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml index 737b2b60ac60..9653b923224a 100644 --- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"ਬੰਦ"</item> <item msgid="460891964396502657">"ਚਾਲੂ"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"ਉਪਲਬਧ ਨਹੀਂ"</item> + <item msgid="8014986104355098744">"ਬੰਦ"</item> + <item msgid="5966994759929723339">"ਚਾਲੂ"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 74ccd5082e47..c06b66347bf7 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -228,6 +228,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autoobracanie"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Autoobracanie ekranu"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokalizacja"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Wygaszacz ekranu"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Dostęp do aparatu"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Dostęp do mikrofonu"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Odblokowany"</string> @@ -355,6 +356,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcesz kontynuować sesję?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Rozpocznij nową"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Tak, kontynuuj"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Tryb gościa"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Jesteś w trybie gościa"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dodanie nowego użytkownika spowoduje zamknięcie trybu gościa. Wszystkie aplikacje i dane z obecnej sesji gościa zostaną usunięte."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Osiągnięto limit użytkowników"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="few">Możesz dodać maksymalnie <xliff:g id="COUNT">%d</xliff:g> użytkowników.</item> @@ -707,7 +711,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alerty"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Zrzuty ekranu"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Wiadomości"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Aplikacje błyskawiczne"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Konfiguracja"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Pamięć wewnętrzna"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Wskazówki"</string> <string name="instant_apps" msgid="8337185853050247304">"Aplikacje błyskawiczne"</string> @@ -974,4 +979,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm ustawiony"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Aparat i mikrofon są wyłączone"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# powiadomienie}few{# powiadomienia}many{# powiadomień}other{# powiadomienia}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmisja"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Zatrzymaj transmisję aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Jeśli transmitujesz aplikację <xliff:g id="SWITCHAPP">%1$s</xliff:g> lub zmieniasz dane wyjściowe, Twoja obecna transmisja zostanie zakończona"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmisja aplikacji <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Zmień dane wyjściowe"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Brak informacji"</string> </resources> diff --git a/packages/SystemUI/res/values-pl/tiles_states_strings.xml b/packages/SystemUI/res/values-pl/tiles_states_strings.xml index c1a4059f6393..50650986c517 100644 --- a/packages/SystemUI/res/values-pl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pl/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Wyłączony"</item> <item msgid="460891964396502657">"Włączony"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Brak dostępu"</item> + <item msgid="8014986104355098744">"Wyłączono"</item> + <item msgid="5966994759929723339">"Włączono"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 79d429d1279d..7945912fb0b9 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Protetor de tela"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Acesso à câmera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Acesso ao microfone"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponível"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sim, continuar"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo visitante"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Você está no modo visitante"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ao adicionar um novo usuário, o dispositivo vai sair do modo visitante e excluir todos os apps e dados da Sessão de visitante atual."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite de usuários atingido"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuário.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de tela"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mensagens gerais"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configurar"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Armazenamento"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Dicas"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme definido"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A câmera e o microfone estão desativados"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificação}one{# notificação}other{# notificações}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitindo"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se você transmitir o app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou mudar a saída, a transmissão atual será interrompida"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Mudar saída"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconhecido"</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml index abf8749e84d7..bc7efe6a8682 100644 --- a/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Desativado"</item> <item msgid="460891964396502657">"Ativado"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Indisponível"</item> + <item msgid="8014986104355098744">"Desativado"</item> + <item msgid="5966994759929723339">"Ativado"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index dfeb147e819b..eab44136e7c5 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotação auto."</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rodar o ecrã automaticamente"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Proteção de ecrã"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Acesso câmara"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Ac. microfone"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponível"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sim, continuar"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo convidado"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Está no modo convidado"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ao adicionar um novo utilizador, o modo convidado é fechado e todas as apps e dados da sessão de convidado atual são eliminados."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite de utilizadores alcançado"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Pode adicionar até <xliff:g id="COUNT">%d</xliff:g> utilizadores.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de ecrã"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mensagens gerais"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Apps instantâneas"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configuração"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Armazenamento"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Sugestões"</string> <string name="instant_apps" msgid="8337185853050247304">"Apps instantâneas"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme definido"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A câmara e o microfone estão desativados"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificação}other{# notificações}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"A transmitir"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão da app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se transmitir a app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou alterar a saída, a sua transmissão atual é interrompida"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmita a app <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Altere a saída"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconhecida"</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml index c8e557b38f9b..0a0102032c6a 100644 --- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Desativado"</item> <item msgid="460891964396502657">"Ativado"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Indisponível"</item> + <item msgid="8014986104355098744">"Desativado"</item> + <item msgid="5966994759929723339">"Ativado"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 79d429d1279d..7945912fb0b9 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Giro automático"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Giro automático da tela"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Localização"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Protetor de tela"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Acesso à câmera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Acesso ao microfone"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponível"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Sim, continuar"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modo visitante"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Você está no modo visitante"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ao adicionar um novo usuário, o dispositivo vai sair do modo visitante e excluir todos os apps e dados da Sessão de visitante atual."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limite de usuários atingido"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">É possível adicionar até <xliff:g id="COUNT">%d</xliff:g> usuário.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alertas"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturas de tela"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mensagens gerais"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configurar"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Armazenamento"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Dicas"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarme definido"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"A câmera e o microfone estão desativados"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificação}one{# notificação}other{# notificações}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Transmitindo"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Interromper a transmissão do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Se você transmitir o app <xliff:g id="SWITCHAPP">%1$s</xliff:g> ou mudar a saída, a transmissão atual será interrompida"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmitir <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Mudar saída"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Desconhecido"</string> </resources> diff --git a/packages/SystemUI/res/values-pt/tiles_states_strings.xml b/packages/SystemUI/res/values-pt/tiles_states_strings.xml index abf8749e84d7..bc7efe6a8682 100644 --- a/packages/SystemUI/res/values-pt/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Desativado"</item> <item msgid="460891964396502657">"Ativado"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Indisponível"</item> + <item msgid="8014986104355098744">"Desativado"</item> + <item msgid="5966994759929723339">"Ativado"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index ebb9f02a4a51..3715b8e70212 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -227,6 +227,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotire automată"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotirea automată a ecranului"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Locație"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screensaver"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Acces la cameră"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Acces la microfon"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Disponibil"</string> @@ -353,6 +354,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, continuați"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modul pentru invitați"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Folosiți modul pentru invitați"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dacă adăugați un utilizator nou, veți ieși din modul pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Ați atins limita de utilizatori"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="few">Puteți adăuga maximum <xliff:g id="COUNT">%d</xliff:g> utilizatori.</item> @@ -702,7 +706,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Alerte"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Baterie"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Capturi de ecran"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mesaje generale"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Aplicații instantanee"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Configurarea"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Stocare"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Indicii"</string> <string name="instant_apps" msgid="8337185853050247304">"Aplicații instantanee"</string> @@ -967,4 +972,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmă setată"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Camera și microfonul sunt dezactivate"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notificare}few{# notificări}other{# de notificări}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Se difuzează"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Opriți difuzarea <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Dacă difuzați <xliff:g id="SWITCHAPP">%1$s</xliff:g> sau schimbați rezultatul, difuzarea actuală se va opri"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Difuzați <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Schimbați rezultatul"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Necunoscută"</string> </resources> diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml index 1ad0a75f39ba..7b7bb3ac11d8 100644 --- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Dezactivat"</item> <item msgid="460891964396502657">"Activat"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Indisponibil"</item> + <item msgid="8014986104355098744">"Dezactivat"</item> + <item msgid="5966994759929723339">"Activat"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 0ee2457d9501..e444d2ba67d3 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -228,6 +228,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоповорот"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоповорот экрана"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Геолокация"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Заставка"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Доступ к камере"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Доступ к микрофону"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Доступно"</string> @@ -355,6 +356,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продолжить сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Начать заново"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, продолжить"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Гостевой режим"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Включен гостевой режим"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Добавив нового пользователя, вы выйдете из гостевого режима. Все приложения и данные в текущем гостевом сеансе будут удалены."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнут лимит"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Можно добавить не более <xliff:g id="COUNT">%d</xliff:g> пользователя.</item> @@ -707,7 +711,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Оповещения"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Батарея"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Скриншоты"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Сообщения"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Приложения с мгновенным запуском"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Настройка"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Хранилище"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Подсказки"</string> <string name="instant_apps" msgid="8337185853050247304">"Приложения с мгновенным запуском"</string> @@ -974,4 +979,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будильник установлен"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера и микрофон отключены"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# уведомление}one{# уведомление}few{# уведомления}many{# уведомлений}other{# уведомления}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Трансляция"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Остановить трансляцию \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Если вы начнете транслировать \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\" или смените целевое устройство, текущая трансляция прервется."</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Транслировать \"<xliff:g id="SWITCHAPP">%1$s</xliff:g>\""</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Транслировать на другое устройство"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Неизвестно"</string> </resources> diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml index e1ee39fd79ee..6255bd8ee26d 100644 --- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Отключен"</item> <item msgid="460891964396502657">"Включен"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Недоступно"</item> + <item msgid="8014986104355098744">"Отключено"</item> + <item msgid="5966994759929723339">"Включено"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 9dcb14e664b7..12614b075ad4 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ස්වයංක්රීය කරකැවීම"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ස්වයංක්රීයව-භ්රමණය වන තිරය"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ස්ථානය"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"තිර සුරැකුම"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"කැමරා ප්රවේශය"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"මයික් ප්රවේශය"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"තිබේ"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්යද?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ඔව්, දිගටම කරගෙන යන්න"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"ආගන්තුක ප්රකාරය"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"ඔබ ආගන්තුක ප්රකාරයේ සිටී"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"නව පරිශීලකයෙකු එක් කිරීම ආගන්තුක මාදිලියෙන් පිටවී වත්මන් ආගන්තුක සැසියෙන් සියලුම යෙදුම් සහ දත්ත මකනු ඇත."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"පරිශීලක සීමාවට ළඟා විය"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">ඔබට පරිශීලකයින් <xliff:g id="COUNT">%d</xliff:g>ක් දක්වා එක් කළ හැකිය.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"ඇඟවීම්"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"බැටරිය"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"තිර රු"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"පොදු පණිවිඩ"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"ක්ෂණික යෙදුම්"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"පිහිටුවීම"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"ගබඩාව"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"ඉඟි"</string> <string name="instant_apps" msgid="8337185853050247304">"ක්ෂණික යෙදුම්"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"සීනුව සකසන ලදි"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"කැමරාව සහ මයික් ක්රියාවිරහිතයි"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{දැනුම්දීම් #ක්}one{දැනුම්දීම් #ක්}other{දැනුම්දීම් #ක්}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"විකාශනය කරමින්"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> විකාශනය කිරීම නවත්වන්නද?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"ඔබ <xliff:g id="SWITCHAPP">%1$s</xliff:g> විකාශනය කළහොත් හෝ ප්රතිදානය වෙනස් කළහොත්, ඔබගේ වත්මන් විකාශනය නවතිනු ඇත."</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> විකාශනය"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"ප්රතිදානය වෙනස් කරන්න"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"නොදනී"</string> </resources> diff --git a/packages/SystemUI/res/values-si/tiles_states_strings.xml b/packages/SystemUI/res/values-si/tiles_states_strings.xml index 8a16acbb7c57..327e0b92c967 100644 --- a/packages/SystemUI/res/values-si/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-si/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"ක්රියාවිරහිතයි"</item> <item msgid="460891964396502657">"ක්රියාත්මකයි"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"නොමැත"</item> + <item msgid="8014986104355098744">"ක්රියාවිරහිතයි"</item> + <item msgid="5966994759929723339">"ක්රියාත්මකයි"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 5feedfcd17da..2652e57e562a 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -228,6 +228,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatické otáčanie"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatické otáčanie obrazovky"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Poloha"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Šetrič obrazovky"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Prístup ku kamere"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Prístup k mikrofónu"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"K dispozícii"</string> @@ -355,6 +356,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relácii pokračovať?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začať odznova"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Áno, pokračovať"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Režim pre hostí"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Ste v režime pre hostí"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ak pridáte nového používatelia, ukončí sa režim pre hostí a odstránia sa všetky aplikácie a údaje z aktuálnej relácie hosťa."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Dosiahnutý limit počtu používateľov"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="few">Môžete pridať maximálne <xliff:g id="COUNT">%d</xliff:g> používateľov.</item> @@ -707,7 +711,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Upozornenia"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batéria"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Snímky obrazovky"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Všeobecné správy"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Okamžité aplikácie"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Nastavenie"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Úložisko"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Tipy"</string> <string name="instant_apps" msgid="8337185853050247304">"Okamžité aplikácie"</string> @@ -974,4 +979,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Budík je nastavený"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera a mikrofón sú vypnuté"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# upozornenie}few{# upozornenia}many{# notifications}other{# upozornení}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Vysiela"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Chcete zastaviť vysielanie aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ak vysielate aplikáciu <xliff:g id="SWITCHAPP">%1$s</xliff:g> alebo zmeníte výstup, aktuálne vysielanie bude zastavené"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Vysielanie aplikácie <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Zmena výstupu"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Neznáme"</string> </resources> diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml index dcdfb3a901b5..3cbde1c9574c 100644 --- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Vypnuté"</item> <item msgid="460891964396502657">"Zapnuté"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Nedostupné"</item> + <item msgid="8014986104355098744">"Vypnuté"</item> + <item msgid="5966994759929723339">"Zapnuté"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index cfb46ad8cebd..ab24cfb36440 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -228,6 +228,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Samodejno sukanje"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Samodejno sukanje zaslona"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ohranjevalnik zaslona"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Dostop do fotoaparata"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Dostop do mikrofona"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Na voljo"</string> @@ -355,6 +356,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite nadaljevati sejo?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začni znova"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, nadaljuj"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Način za goste"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Ste v načinu za goste"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Če dodate novega uporabnika, se bo način za goste zaprl, aplikacije in podatki v trenutni seji gosta pa bodo izbrisani."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Omejitev uporabnikov je dosežena"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Dodate lahko do <xliff:g id="COUNT">%d</xliff:g> uporabnika.</item> @@ -707,7 +711,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Opozorila"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Baterija"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Posnetki zaslona"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Splošna sporočila"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Nenamestljive aplikacije"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Nastavitev"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Shramba"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Namigi"</string> <string name="instant_apps" msgid="8337185853050247304">"Nenamestljive aplikacije"</string> @@ -974,4 +979,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm je nastavljen."</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Fotoaparat in mikrofon sta izklopljena."</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# obvestilo}one{# obvestilo}two{# obvestili}few{# obvestila}other{# obvestil}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Oddajanje"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Želite ustaviti oddajanje aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Če oddajate aplikacijo <xliff:g id="SWITCHAPP">%1$s</xliff:g> ali spremenite izhod, bo trenutno oddajanje ustavljeno."</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Oddajaj aplikacijo <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Sprememba izhoda"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Neznano"</string> </resources> diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml index f1ebee4f8f92..e720819b4b60 100644 --- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Izklopljeno"</item> <item msgid="460891964396502657">"Vklopljeno"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Ni na voljo"</item> + <item msgid="8014986104355098744">"Izklopljeno"</item> + <item msgid="5966994759929723339">"Vklopljeno"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 58cf4c392262..03f89fbd9f48 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rrotullim automatik"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rrotullimi automatik i ekranit"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Vendndodhja"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Mbrojtësi i ekranit"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Qasja te kamera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Qasja te mikrofoni"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"E disponueshme"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Po, vazhdo"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Modaliteti \"vizitor\""</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Je në modalitetin \"vizitor\""</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Shtimi i një përdoruesi të ri do të të nxjerrë nga modaliteti \"vizitor\" dhe do të fshijë të gjitha aplikacionet dhe të dhënat nga sesioni aktual për vizitorë."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"U arrit kufiri i përdoruesve"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Mund të shtosh deri në <xliff:g id="COUNT">%d</xliff:g> përdorues.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Sinjalizimet"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Bateria"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Pamje ekrani"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mesazhe të përgjithshme"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Aplikacionet e çastit"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Konfigurimi"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Hapësira ruajtëse"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Sugjerimet"</string> <string name="instant_apps" msgid="8337185853050247304">"Aplikacionet e çastit"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmi është caktuar"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera dhe mikrofoni janë joaktivë"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# njoftim}other{# njoftime}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Po transmeton"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Të ndalohet transmetimi i <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Nëse transmeton <xliff:g id="SWITCHAPP">%1$s</xliff:g> ose ndryshon daljen, transmetimi yt aktual do të ndalojë"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Transmeto <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ndrysho daljen"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"I panjohur"</string> </resources> diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml index e24abf64c6f8..7a09f2450354 100644 --- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Joaktiv"</item> <item msgid="460891964396502657">"Aktiv"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Nuk ofrohet"</item> + <item msgid="8014986104355098744">"Joaktiv"</item> + <item msgid="5966994759929723339">"Aktiv"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 467af5cab8c0..fcb2d807e687 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -227,6 +227,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Аутоматска ротација"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Аутоматско ротирање екрана"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Локација"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Чувар екрана"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Приступ камери"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Приступ микрофону"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Доступно"</string> @@ -353,6 +354,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Желите ли да наставите сесију?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни из почетка"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Да, настави"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Режим госта"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Користите режим госта"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Додавањем новог корисника изаћи ћете из режима госта и избрисаћете све апликације и податке из актуелне сесије госта."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Достигнут максимални број корисника"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Можете да додате највише <xliff:g id="COUNT">%d</xliff:g> корисника.</item> @@ -702,7 +706,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Обавештења"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Батерија"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Снимци екрана"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Опште поруке"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Инстант апликације"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Подешавање"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Меморијски простор"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Савети"</string> <string name="instant_apps" msgid="8337185853050247304">"Инстант апликације"</string> @@ -967,4 +972,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Аларм је подешен"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камера и микрофон су искључени"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# обавештење}one{# обавештење}few{# обавештења}other{# обавештења}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Емитовање"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Желите да зауставите емитовање апликације <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ако емитујете апликацију <xliff:g id="SWITCHAPP">%1$s</xliff:g> или промените излаз, актуелно емитовање ће се зауставити"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Емитујте апликацију <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Промените излаз"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Непознато"</string> </resources> diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml index 0cef5a66c232..dace491993ba 100644 --- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Искључено"</item> <item msgid="460891964396502657">"Укључено"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Недоступно"</item> + <item msgid="8014986104355098744">"Искључено"</item> + <item msgid="5966994759929723339">"Укључено"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 9273c8581f14..409905a315ff 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Rotera automatiskt"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Rotera skärmen automatiskt"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Plats"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Skärmsläckare"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraåtkomst"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonåtkomst"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Tillgänglig"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ja, fortsätt"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Gästläge"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Du är i gästläge"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Om du lägger till en ny användare avslutas gästläget och alla appar och all data från den aktuella gästsessionen raderas."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Användargränsen har nåtts"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Det går att lägga till upp till <xliff:g id="COUNT">%d</xliff:g> användare.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Aviseringar"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batteri"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Skärmbilder"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Allmänna meddelanden"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Konfigurering"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Lagring"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Tips"</string> <string name="instant_apps" msgid="8337185853050247304">"Snabbappar"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarmet är aktiverat"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kameran och mikrofonen är avstängda"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# avisering}other{# aviseringar}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Sänder"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Vill du sluta sända från <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Om en utsändning från <xliff:g id="SWITCHAPP">%1$s</xliff:g> pågår eller om du byter ljudutgång avbryts den nuvarande utsändningen"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Sänd från <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Byt ljudutgång"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Okänt"</string> </resources> diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml index 410a6bc4645f..9e69b00adf64 100644 --- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Av"</item> <item msgid="460891964396502657">"På"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Inte tillgängligt"</item> + <item msgid="8014986104355098744">"Av"</item> + <item msgid="5966994759929723339">"På"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index ba393de426fb..885e9bdf816c 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Zungusha kiotomatiki"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Skrini ijizungushe kiotomatiki"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Mahali"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Taswira ya skrini"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Ufikiaji wa kamera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Ufikiaji wa maikrofoni"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Unapatikana"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza upya"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ndiyo, endelea"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Matumizi ya wageni"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Unatumia hali ya wageni"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Kuongeza mtumiaji mpya kutaondoa matumizi ya wageni yaliyopo na kufuta programu na data kutoka kwenye kipindi cha mgeni cha sasa."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Umefikia kima cha juu cha watumiaji"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Unaruhusiwa kuongeza hadi watumiaji <xliff:g id="COUNT">%d</xliff:g>.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Arifa"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Betri"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Picha za skrini"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Ujumbe wa Jumla"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Programu zinazofunguka papo hapo"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Weka mipangilio"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Hifadhi"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Vidokezo"</string> <string name="instant_apps" msgid="8337185853050247304">"Programu Zinazofunguka Papo Hapo"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Kengele imewekwa"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera na maikrofoni zimezimwa"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{Arifa #}other{Arifa #}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Inaarifu"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Ungependa kusimamisha utangazaji kwenye <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Ikiwa unatangaza kwenye <xliff:g id="SWITCHAPP">%1$s</xliff:g> au unabadilisha maudhui, tangazo lako la sasa litasimamishwa"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Tangaza kwenye <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Badilisha maudhui"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Haijulikani"</string> </resources> diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml index 79d29ab7b11e..2f765ef5320a 100644 --- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Imezimwa"</item> <item msgid="460891964396502657">"Imewashwa"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Haipatikani"</item> + <item msgid="8014986104355098744">"Imezimwa"</item> + <item msgid="5966994759929723339">"Imewashwa"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml index 6c42073b8805..d638c9d5742e 100644 --- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml @@ -47,6 +47,8 @@ <dimen name="lockscreen_shade_max_over_scroll_amount">32dp</dimen> + <dimen name="status_view_margin_horizontal">8dp</dimen> + <!-- Distance that the full shade transition takes in order to complete by tapping on a button like "expand". --> <dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen> diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml index 589d12f95f45..347cf2965c77 100644 --- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml @@ -21,5 +21,10 @@ <dimen name="keyguard_status_view_bottom_margin">40dp</dimen> <dimen name="bouncer_user_switcher_y_trans">20dp</dimen> + <!-- qs_tiles_page_horizontal_margin should be margin / 2, otherwise full space between two + pages is margin * 2, and that makes tiles page not appear immediately after user swipes to + the side --> + <dimen name="qs_tiles_page_horizontal_margin">32dp</dimen> + <dimen name="qqs_layout_padding_bottom">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml index f45f106d391d..868c003d99a5 100644 --- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml @@ -25,6 +25,8 @@ <dimen name="status_bar_header_height_keyguard">56dp</dimen> + <dimen name="status_view_margin_horizontal">24dp</dimen> + <dimen name="qs_media_session_height_expanded">251dp</dimen> <dimen name="qs_content_horizontal_padding">40dp</dimen> <dimen name="qs_horizontal_margin">40dp</dimen> diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml index 2abc9e3d6119..4de7bb7ef100 100644 --- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml @@ -29,5 +29,11 @@ <dimen name="notification_panel_margin_horizontal">80dp</dimen> <dimen name="notification_side_paddings">40dp</dimen> + + <!-- qs_tiles_page_horizontal_margin should be margin / 2, otherwise full space between two + pages is margin * 2, and that makes tiles page not appear immediately after user swipes to the + side --> + <dimen name="qs_tiles_page_horizontal_margin">60dp</dimen> + <dimen name="notification_section_divider_height">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 08bdb85ba661..7196f5da3fc0 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"தானாகச் சுழற்று"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"திரையைத் தானாகச் சுழற்று"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"இருப்பிடம்"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"ஸ்கிரீன் சேவர்"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"கேமரா அணுகல்"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"மைக் அணுகல்"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"கிடைக்கிறது"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"உங்கள் அமர்வைத் தொடர விருப்பமா?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"மீண்டும் தொடங்கு"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"தொடரவும்"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"கெஸ்ட் பயன்முறை"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"கெஸ்ட் பயன்முறையில் உள்ளீர்கள்"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"புதிய பயனரைச் சேர்த்தால் கெஸ்ட் பயன்முறையில் இருந்து வெளியேற்றப்படுவீர்கள். மேலும் தற்போதைய கெஸ்ட் அமர்வின் ஆப்ஸ் மற்றும் தரவு அனைத்தும் நீக்கப்படும்."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"பயனர் வரம்பை அடைந்துவிட்டீர்கள்"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> பயனர்கள் வரை சேர்க்க முடியும்.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"விழிப்பூட்டல்கள்"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"பேட்டரி"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"ஸ்கிரீன் ஷாட்டுகள்"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"பொதுச் செய்திகள்"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"இன்ஸ்டண்ட் ஆப்ஸ்"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"அமைவு"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"சேமிப்பிடம்"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"குறிப்புகள்"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"அலாரம் அமைக்கப்பட்டுள்ளது"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"கேமராவும் மைக்கும் ஆஃப் செய்யப்பட்டுள்ளன"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# அறிவிப்பு}other{# அறிவிப்புகள்}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ஒலிபரப்புதல்"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் ஒலிபரப்பப்படுவதை நிறுத்தவா?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"நீங்கள் <xliff:g id="SWITCHAPP">%1$s</xliff:g> ஆப்ஸை ஒலிபரப்பினாலோ அவுட்புட்டை மாற்றினாலோ உங்களின் தற்போதைய ஒலிபரப்பு நிறுத்தப்படும்"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ஆப்ஸை ஒலிபரப்பு"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"அவுட்புட்டை மாற்று"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"தெரியவில்லை"</string> </resources> diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml index 52fca126922e..41f64125753c 100644 --- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"முடக்கப்பட்டுள்ளது"</item> <item msgid="460891964396502657">"இயக்கப்பட்டுள்ளது"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"கிடைக்கவில்லை"</item> + <item msgid="8014986104355098744">"முடக்கப்பட்டுள்ளது"</item> + <item msgid="5966994759929723339">"இயக்கப்பட்டுள்ளது"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 3e653ba596e6..cac9cfdf46b9 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ఆటో-రొటేట్"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"స్క్రీన్ ఆటో-రొటేట్"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"లొకేషన్"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"స్క్రీన్ సేవర్"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"కెమెరా యాక్సెస్"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"మైక్ యాక్సెస్"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"అందుబాటులో ఉంది"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్ని కొనసాగించాలనుకుంటున్నారా?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"అవును, కొనసాగించు"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"గెస్ట్ మోడ్"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"మీరు గెస్ట్ మోడ్లో ఉన్నారు"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"కొత్త యూజర్ను జోడించడం వలన గెస్ట్ మోడ్ నుండి వైదొలుగుతుంది. అలాగే ప్రస్తుత గెస్ట్ సెషన్ నుండి అన్ని యాప్లతో పాటు మొత్తం డేటా తొలగించబడుతుంది."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"వినియోగదారు పరిమితిని చేరుకున్నారు"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">మీరు <xliff:g id="COUNT">%d</xliff:g> వినియోగదారుల వరకు జోడించవచ్చు.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"అలర్ట్లు"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"బ్యాటరీ"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"స్క్రీన్షాట్లు"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"సాధారణ మెసేజ్లు"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"ఇన్స్టంట్ యాప్లు"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"సెటప్ చేయండి"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"స్టోరేజ్"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"సూచనలు"</string> <string name="instant_apps" msgid="8337185853050247304">"ఇన్స్టంట్ యాప్లు"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"అలారం సెట్ చేశాను"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"కెమెరా, మైక్ ఆఫ్లో ఉన్నాయి"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# నోటిఫికేషన్}other{# నోటిఫికేషన్లు}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"ప్రసారం చేస్తోంది"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ప్రసారం చేయడాన్ని ఆపివేయాలా?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"మీరు <xliff:g id="SWITCHAPP">%1$s</xliff:g> ప్రసారం చేస్తే లేదా అవుట్పుట్ను మార్చినట్లయితే, మీ ప్రస్తుత ప్రసారం ఆగిపోతుంది"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ప్రసారం చేయండి"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"అవుట్పుట్ను మార్చండి"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"తెలియదు"</string> </resources> diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml index 60997928082e..44ba47781ae7 100644 --- a/packages/SystemUI/res/values-te/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"ఆఫ్"</item> <item msgid="460891964396502657">"ఆన్"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"అందుబాటులో లేరు"</item> + <item msgid="8014986104355098744">"ఆఫ్"</item> + <item msgid="5966994759929723339">"ఆన్"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 09095bba71d9..73a7350e7782 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"หมุนอัตโนมัติ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"หมุนหน้าจออัตโนมัติ"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"ตำแหน่ง"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"โปรแกรมรักษาหน้าจอ"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"สิทธิ์เข้าถึงกล้อง"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"สิทธิ์เข้าถึงไมโครโฟน"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"พร้อมให้ใช้งาน"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ใช่ ดำเนินการต่อ"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"โหมดผู้ใช้ชั่วคราว"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"คุณอยู่ในโหมดผู้ใช้ชั่วคราว"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"การเพิ่มผู้ใช้ใหม่จะเป็นการออกจากโหมดผู้ใช้ชั่วคราว และจะลบแอปและข้อมูลทั้งหมดจากเซสชันผู้ใช้ชั่วคราวในปัจจุบัน"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"ถึงขีดจำกัดผู้ใช้แล้ว"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">คุณเพิ่มผู้ใช้ได้สูงสุด <xliff:g id="COUNT">%d</xliff:g> คน</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"การแจ้งเตือน"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"แบตเตอรี"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"ภาพหน้าจอ"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"ข้อความทั่วไป"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"ตั้งค่า"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"พื้นที่เก็บข้อมูล"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"คำแนะนำ"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant App"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"ตั้งปลุกแล้ว"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"กล้องและไมค์ปิดอยู่"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{การแจ้งเตือน # รายการ}other{การแจ้งเตือน # รายการ}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"กำลังออกอากาศ"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"หยุดการออกอากาศ <xliff:g id="APP_NAME">%1$s</xliff:g> ไหม"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"หากคุณออกอากาศ <xliff:g id="SWITCHAPP">%1$s</xliff:g> หรือเปลี่ยนแปลงเอาต์พุต การออกอากาศในปัจจุบันจะหยุดลง"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"ออกอากาศ <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"เปลี่ยนเอาต์พุต"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"ไม่ทราบ"</string> </resources> diff --git a/packages/SystemUI/res/values-th/tiles_states_strings.xml b/packages/SystemUI/res/values-th/tiles_states_strings.xml index 4565f35fbef2..9cd060f2cabf 100644 --- a/packages/SystemUI/res/values-th/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-th/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"ปิด"</item> <item msgid="460891964396502657">"เปิด"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"ไม่พร้อมใช้งาน"</item> + <item msgid="8014986104355098744">"ปิด"</item> + <item msgid="5966994759929723339">"เปิด"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 3144e353184b..1d5c0883ee86 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"I-auto rotate"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Awtomatikong i-rotate ang screen"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokasyon"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Screen saver"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Access sa camera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Access sa mic"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Available"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Oo, magpatuloy"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Guest mode"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Naka-guest mode ka"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Kapag nagdagdag ka ng bagong user, aalis sa guest mode at made-delete ang lahat ng app at data mula sa kasalukuyang session ng bisita."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Naabot na ang limitasyon sa user"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Maaari kang magdagdag ng hanggang <xliff:g id="COUNT">%d</xliff:g> user.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Mga Alerto"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Baterya"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Mga Screenshot"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Mga Pangkalahatang Mensahe"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Instant Apps"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Setup"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Storage"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Mga Hint"</string> <string name="instant_apps" msgid="8337185853050247304">"Instant Apps"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Nakatakda ang alarm"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Naka-off ang camera at mikropono"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# notification}one{# notification}other{# na notification}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Nagbo-broadcast"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Ihinto ang pag-broadcast ng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Kung magbo-broadcast ka ng <xliff:g id="SWITCHAPP">%1$s</xliff:g> o babaguhin mo ang output, hihinto ang iyong kasalukuyang broadcast"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"I-broadcast ang <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Baguhin ang output"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Hindi alam"</string> </resources> diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml index 59fed0f6b247..cd7dcf51b279 100644 --- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Naka-off"</item> <item msgid="460891964396502657">"Naka-on"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Hindi available"</item> + <item msgid="8014986104355098744">"Naka-off"</item> + <item msgid="5966994759929723339">"Naka-on"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 27339537ed29..dcdb604a374e 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Otomatik döndür"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranı otomatik döndür"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Konum"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekran koruyucu"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kamera erişimi"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofon erişimi"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Kullanılabilir"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Evet, devam et"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Misafir modu"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Misafir modundasınız"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Yeni bir kullanıcı eklendiğinde misafir modundan çıkılarak mevcut misafir oturumundaki tüm uygulamalar ve veriler silinir."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Kullanıcı sınırına ulaşıldı"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">En fazla <xliff:g id="COUNT">%d</xliff:g> kullanıcı ekleyebilirsiniz.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Uyarılar"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Pil"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Ekran görüntüleri"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Genel Mesajlar"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Hazır Uygulamalar"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Kurulum"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Depolama alanı"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"İpuçları"</string> <string name="instant_apps" msgid="8337185853050247304">"Hazır Uygulamalar"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Alarm kuruldu"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera ve mikrofon kapalı"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# bildirim}other{# bildirim}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Yayınlama"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasında anons durdurulsun mu?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uygulamasında anons yapar veya çıkışı değiştirirseniz mevcut anonsunuz duraklatılır"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> uygulamasında anons yapın"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Çıkışı değiştirme"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Bilinmiyor"</string> </resources> diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml index d06d72715d44..28ba7dcb9010 100644 --- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Kapalı"</item> <item msgid="460891964396502657">"Açık"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Kullanılamıyor"</item> + <item msgid="8014986104355098744">"Kapalı"</item> + <item msgid="5966994759929723339">"Açık"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 4550d17bb54e..a86917069734 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -228,6 +228,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Автоматичне обертання"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Автоматично обертати екран"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Геодані"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Заставка"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Доступ до камери"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Доступ до мікрофона"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Дозволено"</string> @@ -355,6 +356,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продовжити сеанс?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почати знову"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Так, продовжити"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Режим гостя"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Ви ввійшли в режимі гостя"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Якщо додати нового користувача, ви вийдете з режиму гостя, а всі додатки й дані з поточного сеансу цього режиму буде видалено."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Ви досягли ліміту користувачів"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Можна додати до <xliff:g id="COUNT">%d</xliff:g> користувача.</item> @@ -707,7 +711,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Сповіщення"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Акумулятор"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Знімки екрана"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Загальні повідомлення"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Додатки з миттєвим запуском"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Налаштування"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Пам’ять"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Поради"</string> <string name="instant_apps" msgid="8337185853050247304">"Додатки з миттєвим запуском"</string> @@ -974,4 +979,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Будильник установлено"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Камеру й мікрофон вимкнено"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# сповіщення}one{# сповіщення}few{# сповіщення}many{# сповіщень}other{# сповіщення}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Трансляція"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Зупинити трансляцію з додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Якщо ви зміните додаток (<xliff:g id="SWITCHAPP">%1$s</xliff:g>) або аудіовихід, поточну трансляцію буде припинено"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Змінити додаток для трансляції на <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Змінити аудіовихід"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Невідомо"</string> </resources> diff --git a/packages/SystemUI/res/values-uk/tiles_states_strings.xml b/packages/SystemUI/res/values-uk/tiles_states_strings.xml index 040fb4d3d6e3..3f6ca461ac1d 100644 --- a/packages/SystemUI/res/values-uk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-uk/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Вимкнено"</item> <item msgid="460891964396502657">"Увімкнено"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Недоступно"</item> + <item msgid="8014986104355098744">"Вимкнено"</item> + <item msgid="5966994759929723339">"Увімкнено"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 96ea191b46fc..717dafbd8402 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"خود کار طور پر گھمائیں"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"اسکرین کو خود کار طور پر گھمائیں"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"مقام"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"اسکرین سیور"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"کیمرا تک رسائی"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"مائیکروفون تک رسائی"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"دستیاب ہے"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"کیا آپ اپنا سیشن جاری رکھنا چاہتے ہیں؟"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"دوبارہ شروع کریں"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"ہاں، جاری رکھیں"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"مہمان وضع"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"آپ مہمان وضع میں ہیں"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"نئے صارف کو شامل کرنے سے مہمان وضع سے باہر نکل جائے گا اور موجودہ مہمان سیشن سے تمام ایپس اور ڈیٹا حذف ہو جائیں گے۔"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"صارف کی حد مکمل ہو گئی"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">صرف <xliff:g id="COUNT">%d</xliff:g> صارفین بنائے جا سکتے ہیں۔</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"الرٹس"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"بیٹری"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"اسکرین شاٹس"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"عمومی پیغامات"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"فوری ایپس"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"سیٹ اپ"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"اسٹوریج"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"اشارات"</string> <string name="instant_apps" msgid="8337185853050247304">"فوری ایپس"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"الارم سیٹ ہوگیا"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"کیمرا اور مائیک آف ہیں"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# اطلاع}other{# اطلاعات}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"نشریات"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> براڈکاسٹنگ روکیں؟"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"اگر آپ <xliff:g id="SWITCHAPP">%1$s</xliff:g> براڈکاسٹ کرتے ہیں یا آؤٹ پٹ کو تبدیل کرتے ہیں تو آپ کا موجودہ براڈکاسٹ رک جائے گا"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> پر براڈکاسٹ کریں"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"آؤٹ پٹ تبدیل کریں"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"نامعلوم"</string> </resources> diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml index 79d2cf6d0624..05aa4e91e5cc 100644 --- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"آف"</item> <item msgid="460891964396502657">"آن"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"غیر دستیاب"</item> + <item msgid="8014986104355098744">"آف"</item> + <item msgid="5966994759929723339">"آن"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index a6f29389c194..65602d24661c 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Avto-burilish"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Ekranning avtomatik burilishi"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Joylashuv"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Ekran lavhasi"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Kameraga ruxsat"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonga ruxsat"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Mavjud"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Seansni davom ettirmoqchimisiz?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Boshidan boshlansin"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Ha, davom ettirilsin"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Mehmon rejimi"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Mehmon rejimidasiz"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Yangi foydalanuvchi kiritilsa, mehmon rejimi tark etiladi va joriy mehmon seansidagi barcha ilova va ularning maʼlumotlari tozalanadi."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Limitga yetib keldi"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other"><xliff:g id="COUNT">%d</xliff:g> tagacha foydalanuvchi qo‘shish mumkin.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Bildirishnomalar"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Batareya"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Skrinshotlar"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Umumiy xabarlar"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Darhol ochiladigan ilovalar"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Sozlash"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Xotira"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Maslahatlar"</string> <string name="instant_apps" msgid="8337185853050247304">"Darhol ochiladigan ilovalar"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Signal oʻrnatildi"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Kamera va mikrofon yoqilmagan"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# ta bildirishnoma}other{# ta bildirishnoma}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Signal uzatish"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga translatsiya toʻxtatilsinmi?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Agar <xliff:g id="SWITCHAPP">%1$s</xliff:g> ilovasiga translatsiya qilsangiz yoki ovoz chiqishini oʻzgartirsangiz, joriy translatsiya toʻxtab qoladi"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ilovasiga translatsiya"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Ovoz chiqishini oʻzgartirish"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Noaniq"</string> </resources> diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml index b6875974d2de..a84f7698d861 100644 --- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Oʻchiq"</item> <item msgid="460891964396502657">"Yoniq"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Bandman"</item> + <item msgid="8014986104355098744">"Oʻchiq"</item> + <item msgid="5966994759929723339">"Yoniq"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index c8ffb03ebee6..1fda576f1e4c 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Tự động xoay"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Tự động xoay màn hình"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Vị trí"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Trình bảo vệ màn hình"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Truy cập máy ảnh"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Truy cập micrô"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Được phép"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Bạn có muốn tiếp tục phiên của mình không?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Bắt đầu lại"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Có, tiếp tục"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Chế độ khách"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Bạn đang ở chế độ khách"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Khi thêm người dùng mới, chế độ khách sẽ bị thoát và mọi ứng dụng cũng như dữ liệu trong phiên khách hiện tại sẽ bị xoá."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Đã đạt đến giới hạn người dùng"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">Bạn có thể thêm tối đa <xliff:g id="COUNT">%d</xliff:g> người dùng.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Cảnh báo"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Pin"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Ảnh chụp màn hình"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Thông báo chung"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Ứng dụng tức thì"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Thiết lập"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Bộ nhớ"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Gợi ý"</string> <string name="instant_apps" msgid="8337185853050247304">"Ứng dụng tức thì"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"Đã đặt chuông báo"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Máy ảnh và micrô đang tắt"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# thông báo}other{# thông báo}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Phát sóng"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Dừng phát <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Nếu bạn phát <xliff:g id="SWITCHAPP">%1$s</xliff:g> hoặc thay đổi đầu ra, phiên truyền phát hiện tại sẽ dừng"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Phát <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Thay đổi đầu ra"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Không xác định"</string> </resources> diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml index 827c72ae2867..482a32f902b4 100644 --- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Đang tắt"</item> <item msgid="460891964396502657">"Đang bật"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Không có sẵn"</item> + <item msgid="8014986104355098744">"Tắt"</item> + <item msgid="5966994759929723339">"Đang bật"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index dd04f925858c..a073abf586fe 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自动旋转屏幕"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自动旋转屏幕"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"位置信息"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"屏保"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"摄像头使用权限"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"麦克风使用权限"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"已允许"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"是,继续"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"访客模式"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"您当前处于访客模式"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"如果添加新用户,系统将退出访客模式并删除当前访客会话中的所有应用和数据。"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"已达到用户数上限"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">您最多可以添加 <xliff:g id="COUNT">%d</xliff:g> 位用户。</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"提醒"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"电池"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"屏幕截图"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"常规消息"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"免安装应用"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"设置"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"存储空间"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"提示"</string> <string name="instant_apps" msgid="8337185853050247304">"免安装应用"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"闹钟已设置"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"摄像头和麦克风已关闭"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 条通知}other{# 条通知}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"正在广播"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止广播“<xliff:g id="APP_NAME">%1$s</xliff:g>”的内容吗?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如果广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容或更改输出来源,当前的广播就会停止"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"广播“<xliff:g id="SWITCHAPP">%1$s</xliff:g>”的内容"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"更改输出来源"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"未知"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml index 3c628721aa5b..b476255bc669 100644 --- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"已关闭"</item> <item msgid="460891964396502657">"已开启"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"不可用"</item> + <item msgid="8014986104355098744">"关闭"</item> + <item msgid="5966994759929723339">"开启"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 0ea868ae78c2..edf79fe96138 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"位置"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"螢幕保護程式"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"相機存取權"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"麥克風存取權"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"允許"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"是的,請繼續"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"訪客模式"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"您正在使用訪客模式"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"新增使用者後,系統就會結束訪客模式,並刪除目前訪客工作階段中的所有應用程式和資料。"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"已達到使用者上限"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">您可以加入多達 <xliff:g id="COUNT">%d</xliff:g> 個使用者。</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"通知"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"電池"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"螢幕擷取畫面"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"一般訊息"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"免安裝應用程式"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"設定"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"儲存空間"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"提示"</string> <string name="instant_apps" msgid="8337185853050247304">"免安裝應用程式"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"已設定鬧鐘"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"相機和麥克風已關閉"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 則通知}other{# 則通知}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"廣播"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止廣播「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如要廣播「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容或變更輸出來源,系統就會停止廣播目前的內容"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"廣播「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"變更輸出來源"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"不明"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml index ee4106639a67..ab8e961a1f47 100644 --- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"關閉"</item> <item msgid="460891964396502657">"開啟"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"無法使用"</item> + <item msgid="8014986104355098744">"已關閉"</item> + <item msgid="5966994759929723339">"已開啟"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 23655a74165f..5282af465fb9 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"自動旋轉"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"自動旋轉螢幕"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"定位"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"螢幕保護程式"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"相機存取權"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"麥克風存取權"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"可以使用"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"你要繼續這個工作階段嗎?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"是,繼續"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"訪客模式"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"你目前處於訪客模式"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"新增使用者後,系統就會結束訪客模式,並刪除目前訪客工作階段中的所有應用程式和資料。"</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"已達使用者數量上限"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="other">最多可新增 <xliff:g id="COUNT">%d</xliff:g> 位使用者。</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"快訊"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"電池"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"螢幕截圖"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"一般訊息"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"免安裝應用程式"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"設定"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"儲存空間"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"提示"</string> <string name="instant_apps" msgid="8337185853050247304">"免安裝應用程式"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"鬧鐘設定成功"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"已關閉相機和麥克風"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{# 則通知}other{# 則通知}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"廣播"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"要停止播送「<xliff:g id="APP_NAME">%1$s</xliff:g>」的內容嗎?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"如果播送「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容或變更輸出來源,系統就會停止播送目前的內容"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"播送「<xliff:g id="SWITCHAPP">%1$s</xliff:g>」的內容"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"變更輸出來源"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"不明"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml index 1f707408b95b..3d6a546e6103 100644 --- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"已關閉"</item> <item msgid="460891964396502657">"已開啟"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"無法使用"</item> + <item msgid="8014986104355098744">"已關閉"</item> + <item msgid="5966994759929723339">"已開啟"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 34646f37727e..a6407928709f 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -226,6 +226,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Ukuphenduka okuzenzakalelayo"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Phendula iskrini ngokuzenzakalela"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Indawo"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Isilondolozi sesikrini"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Ukufinyelela kwekhamera"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Ukufinyelela kwe-mic"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Iyatholakala"</string> @@ -351,6 +352,9 @@ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ingabe ufuna ukuqhubeka ngesikhathi sakho?"</string> <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Qala phansi"</string> <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Yebo, qhubeka"</string> + <string name="guest_notification_app_name" msgid="2110425506754205509">"Imodi yesivakashi"</string> + <string name="guest_notification_session_active" msgid="5567273684713471450">"Usemodini yesivakashi"</string> + <string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Ukwengeza umsebenzisi omusha kuzokhipha imodi yesivakashi futhi kusule wonke ama-app nedatha kusuka esikhathini sesihambeli samanje."</string> <string name="user_limit_reached_title" msgid="2429229448830346057">"Kufinyelelwe kumkhawulo womsebenzisi"</string> <plurals name="user_limit_reached_message" formatted="false" msgid="2573535787802908398"> <item quantity="one">Ungangeza kufikela kubasebenzisi abangu-<xliff:g id="COUNT">%d</xliff:g>.</item> @@ -697,7 +701,8 @@ <string name="notification_channel_alerts" msgid="3385787053375150046">"Izexwayiso"</string> <string name="notification_channel_battery" msgid="9219995638046695106">"Ibhethri"</string> <string name="notification_channel_screenshot" msgid="7665814998932211997">"Izithombe-skrini"</string> - <string name="notification_channel_general" msgid="4384774889645929705">"Imilayezo ejwayelekile"</string> + <string name="notification_channel_instant" msgid="7556135423486752680">"Ama-App Asheshayo"</string> + <string name="notification_channel_setup" msgid="7660580986090760350">"Ukusetha"</string> <string name="notification_channel_storage" msgid="2720725707628094977">"Isitoreji"</string> <string name="notification_channel_hints" msgid="7703783206000346876">"Ukubonisa"</string> <string name="instant_apps" msgid="8337185853050247304">"Izinhlelo zokusebenza ezisheshayo"</string> @@ -960,4 +965,10 @@ <string name="dream_overlay_status_bar_alarm_set" msgid="566707328356590886">"I-alamu isethiwe"</string> <string name="dream_overlay_status_bar_camera_mic_off" msgid="3199425257833773569">"Ikhamera nemakrofoni kuvaliwe"</string> <string name="dream_overlay_status_bar_notification_indicator" msgid="8091389255691081711">"{count,plural, =1{Isaziso esingu-#}one{Izaziso ezingu-#}other{Izaziso ezingu-#}}"</string> + <string name="broadcasting_description_is_broadcasting" msgid="765627502786404290">"Ukusakaza"</string> + <string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Misa ukusakaza i-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Uma usakaza i-<xliff:g id="SWITCHAPP">%1$s</xliff:g> noma ushintsha okuphumayo, ukusakaza kwakho kwamanje kuzoma"</string> + <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Sakaza i-<xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string> + <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Shintsha okuphumayo"</string> + <string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Akwaziwa"</string> </resources> diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml index cc8bbb071449..81c46364a9fd 100644 --- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml @@ -171,4 +171,9 @@ <item msgid="146088982397753810">"Valiwe"</item> <item msgid="460891964396502657">"Vuliwe"</item> </string-array> + <string-array name="tile_states_dream"> + <item msgid="6184819793571079513">"Ayitholakali"</item> + <item msgid="8014986104355098744">"Valiwe"</item> + <item msgid="5966994759929723339">"Vuliwe"</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index ed3a825de28f..5cb7d46d20a6 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -87,7 +87,7 @@ <!-- Tiles native to System UI. Order should match "quick_settings_tiles_default" --> <string name="quick_settings_tiles_stock" translatable="false"> - internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction + internet,bt,flashlight,dnd,alarm,airplane,controls,wallet,rotation,battery,cast,screenrecord,mictoggle,cameratoggle,location,hotspot,inversion,saver,dark,work,night,reverse,reduce_brightness,qr_code_scanner,onehanded,color_correction,dream </string> <!-- The tiles to display in QuickSettings --> @@ -438,6 +438,9 @@ they were added. --> <integer name="config_smart_replies_in_notifications_onclick_init_delay">200</integer> + <!-- Smartspace trampoline activity that is used when the user taps smartspace. --> + <string name="config_smartspaceTrampolineActivityComponent" translatable="false">com.google.android.apps.gsa.staticplugins.opa.smartspace.ExportedSmartspaceTrampolineActivity</string> + <!-- Screenshot editing default activity. Must handle ACTION_EDIT image/png intents. Blank sends the user to the Chooser first. This name is in the ComponentName flattened format (package/class) --> @@ -625,11 +628,11 @@ <!-- This value is used when calculating whether the device is in ambient light mode. It is light mode when the light sensor sample value exceeds above this value. --> - <integer name="config_ambientLightModeThreshold">5</integer> + <integer name="config_ambientLightModeThreshold">10</integer> <!-- This value is used when calculating whether the device is in ambient dark mode. It is dark mode when the light sensor sample value drops below this value. --> - <integer name="config_ambientDarkModeThreshold">2</integer> + <integer name="config_ambientDarkModeThreshold">5</integer> <!-- This value is used when calculating whether the device is in ambient light mode. Each sample contains light sensor events from this span of time duration. --> @@ -693,7 +696,7 @@ <string name="config_communalSourceConnector" translatable="false"></string> <!-- How often in milliseconds to jitter the dream overlay in order to avoid burn-in. --> - <integer name="config_dreamOverlayBurnInProtectionUpdateIntervalMillis">500</integer> + <integer name="config_dreamOverlayBurnInProtectionUpdateIntervalMillis">1000</integer> <!-- How long in milliseconds before full burn-in protection is achieved. --> <integer name="config_dreamOverlayMillisUntilFullJitter">240000</integer> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 40664992ba59..0aeed5e3914b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -48,7 +48,50 @@ <!-- The minimum display position of the arrow on the screen --> <dimen name="navigation_edge_arrow_min_y">64dp</dimen> <!-- The amount by which the arrow is shifted to avoid the finger--> - <dimen name="navigation_edge_finger_offset">48dp</dimen> + <dimen name="navigation_edge_finger_offset">64dp</dimen> + + <!-- The thickness of the arrow --> + <dimen name="navigation_edge_arrow_thickness">4dp</dimen> + <!-- The minimum delta needed to change direction / stop triggering back --> + <dimen name="navigation_edge_minimum_x_delta_for_switch">32dp</dimen> + + <!-- entry state --> + <dimen name="navigation_edge_entry_margin">4dp</dimen> + <dimen name="navigation_edge_entry_background_width">8dp</dimen> + <dimen name="navigation_edge_entry_background_height">60dp</dimen> + <dimen name="navigation_edge_entry_edge_corners">6dp</dimen> + <dimen name="navigation_edge_entry_far_corners">6dp</dimen> + <dimen name="navigation_edge_entry_arrow_length">10dp</dimen> + <dimen name="navigation_edge_entry_arrow_height">7dp</dimen> + + <!-- pre-threshold --> + <dimen name="navigation_edge_pre_threshold_margin">4dp</dimen> + <dimen name="navigation_edge_pre_threshold_background_width">64dp</dimen> + <dimen name="navigation_edge_pre_threshold_background_height">60dp</dimen> + <dimen name="navigation_edge_pre_threshold_edge_corners">22dp</dimen> + <dimen name="navigation_edge_pre_threshold_far_corners">26dp</dimen> + + <!-- post-threshold / active --> + <dimen name="navigation_edge_active_margin">14dp</dimen> + <dimen name="navigation_edge_active_background_width">60dp</dimen> + <dimen name="navigation_edge_active_background_height">60dp</dimen> + <dimen name="navigation_edge_active_edge_corners">30dp</dimen> + <dimen name="navigation_edge_active_far_corners">30dp</dimen> + <dimen name="navigation_edge_active_arrow_length">8dp</dimen> + <dimen name="navigation_edge_active_arrow_height">9dp</dimen> + + <!-- stretch @412 dp --> + <dimen name="navigation_edge_stretch_threshold">412dp</dimen> + <dimen name="navigation_edge_stretch_margin">18dp</dimen> + <dimen name="navigation_edge_stretch_background_width">74dp</dimen> + <dimen name="navigation_edge_stretch_background_height">60dp</dimen> + <dimen name="navigation_edge_stretch_left_corners">30dp</dimen> + <dimen name="navigation_edge_stretch_right_corners">30dp</dimen> + <dimen name="navigation_edge_stretched_arrow_length">7dp</dimen> + <dimen name="navigation_edge_stretched_arrow_height">10dp</dimen> + + <dimen name="navigation_edge_cancelled_arrow_length">12dp</dimen> + <dimen name="navigation_edge_cancelled_arrow_height">0dp</dimen> <!-- Height of notification icons in the status bar --> <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen> @@ -452,7 +495,7 @@ <dimen name="navigation_key_padding">0dp</dimen> <!-- Floating rotation button --> - <dimen name="floating_rotation_button_diameter">40dp</dimen> + <dimen name="floating_rotation_button_diameter">52dp</dimen> <dimen name="floating_rotation_button_min_margin">20dp</dimen> <dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen> <dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen> @@ -1464,4 +1507,18 @@ <dimen name="media_output_broadcast_info_title_height">24dp</dimen> <dimen name="media_output_broadcast_info_summary_height">20dp</dimen> <dimen name="media_output_broadcast_info_edit">18dp</dimen> + + <!-- Broadcast dialog --> + <dimen name="broadcast_dialog_title_img_margin_top">18dp</dimen> + <dimen name="broadcast_dialog_title_text_size">24sp</dimen> + <dimen name="broadcast_dialog_title_text_margin">16dp</dimen> + <dimen name="broadcast_dialog_title_text_margin_top">18dp</dimen> + <dimen name="broadcast_dialog_subtitle_text_size">14sp</dimen> + <dimen name="broadcast_dialog_icon_size">24dp</dimen> + <dimen name="broadcast_dialog_icon_margin_top">25dp</dimen> + <dimen name="broadcast_dialog_btn_radius">100dp</dimen> + <dimen name="broadcast_dialog_btn_margin_bottom">4dp</dimen> + <dimen name="broadcast_dialog_btn_text_size">16sp</dimen> + <dimen name="broadcast_dialog_btn_minHeight">44dp</dimen> + <dimen name="broadcast_dialog_margin">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index fbfd54b909e5..6cd47d24db4e 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -505,10 +505,10 @@ <string name="notification_group_overflow_indicator">+ <xliff:g id="number" example="3">%s</xliff:g></string> <!-- Content description describing how many more notifications are in a group [CHAR LIMIT=NONE] --> - <plurals name="notification_group_overflow_description"> - <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> more notification inside.</item> - <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> more notifications inside.</item> - </plurals> + <string name="notification_group_overflow_description">{count, plural, + =1 {# more notification inside.} + other {# more notifications inside.} + }</string> <!-- Format to use to summarize a message from a contact in a single line of text. For example: "Julia: How's it going?". [CHAR LIMIT=NONE] --> @@ -578,6 +578,8 @@ <!-- QuickSettings: Location [CHAR LIMIT=NONE] --> <string name="quick_settings_location_label">Location</string> <!-- QuickSettings: Location (Off) [CHAR LIMIT=NONE] --> + <!-- QuickSettings: Screen saver [CHAR LIMIT=NONE] --> + <string name="quick_settings_screensaver_label">Screen saver</string> <!-- QuickSettings: Camera [CHAR LIMIT=NONE] --> <string name="quick_settings_camera_label">Camera access</string> <!-- QuickSettings: Microphone [CHAR LIMIT=NONE] --> @@ -652,10 +654,10 @@ the user why they can't toggle the hotspot tile. [CHAR LIMIT=20] --> <string name="quick_settings_hotspot_secondary_label_data_saver_enabled">Data Saver is on</string> <!-- QuickSettings: Hotspot: Secondary label for how many devices are connected to the hotspot [CHAR LIMIT=NONE] --> - <plurals name="quick_settings_hotspot_secondary_label_num_devices"> - <item quantity="one">%d device</item> - <item quantity="other">%d devices</item> - </plurals> + <string name="quick_settings_hotspot_secondary_label_num_devices">{count, plural, + =1 {# device} + other {# devices} + }</string> <!-- QuickSettings: Notifications [CHAR LIMIT=NONE] --> <!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] --> <string name="quick_settings_flashlight_label">Flashlight</string> @@ -911,14 +913,25 @@ <!-- Notification when resuming an existing guest session: Action that continues with the current session [CHAR LIMIT=35] --> <string name="guest_wipe_session_dontwipe">Yes, continue</string> + <!-- App name of the notification when guest mode is entered [CHAR LIMIT=35] --> + <string name="guest_notification_app_name">Guest mode</string> + <!-- Title of the notification when guest mode is entered [CHAR LIMIT=35] --> + <string name="guest_notification_session_active">You are in guest mode</string> + + <!-- Additional message for add user confirmation dialog that is appended when current user is + guest and guest is ephemeral. This is to warn users that current guest session + would get removed after a new user is added and switched to [CHAR LIMIT=none] --> + <string name="user_add_user_message_guest_remove">\n\nAdding a new user will exit guest mode + and delete all apps and data from the current guest session.</string> + <!-- Title for the dialog that lets users know that the maximum allowed number of users on the device has been reached. [CHAR LIMIT=35]--> <string name="user_limit_reached_title">User limit reached</string> <!-- Message that tells people what's the maximum number of uses allowed on the device. [CHAR_LIMIT=NONE]--> - <plurals name="user_limit_reached_message"> - <item quantity="one">Only one user can be created.</item> - <item quantity="other">You can add up to <xliff:g id="count" example="3">%d</xliff:g> users.</item> - </plurals> + <string name="user_limit_reached_message">{count, plural, + =1 {Only one user can be created.} + other {You can add up to # users.} + }</string> <!-- Title of the confirmation dialog for deleting a user [CHAR LIMIT=NONE] --> <string name="user_remove_user_title">Remove user?</string> @@ -1473,20 +1486,20 @@ <!-- Notification: Snooze panel: message indicating how long the notification was snoozed for. [CHAR LIMIT=100]--> <string name="snoozed_for_time">Snoozed for <xliff:g id="time_amount" example="15 minutes">%1$s</xliff:g></string> - <!-- Notification:Snooze panel: Snooze options for hours --> - <plurals name="snoozeHourOptions"> - <item quantity="one">%d hour</item> - <item quantity="two">%d hours</item> - <item quantity="few">%d hours</item> - <item quantity="other">%d hours</item> - </plurals> - - <!-- Notification: Snooze panel: Snooze options for minutes --> - <plurals name="snoozeMinuteOptions"> - <item quantity="one">%d minute</item> - <item quantity="few">%d minutes</item> - <item quantity="other">%d minutes</item> - </plurals> + <!-- Notification:Snooze panel: Snooze options for hours [CHAR LIMIT=NONE]--> + <string name="snoozeHourOptions">{count, plural, + =1 {# hour} + =2 {# hours} + few {# hours} + other {# hours} + }</string> + + <!-- Notification: Snooze panel: Snooze options for minutes [CHAR LIMIT=NONE]--> + <string name="snoozeMinuteOptions">{count, plural, + =1 {# minute} + few {# minutes} + other {# minutes} + }</string> <!-- Summary of battery saver not available [CHAR LIMIT=NONE] --> @@ -1896,8 +1909,10 @@ <string name="notification_channel_battery">Battery</string> <!-- Title for the notification channel dedicated to screenshot progress. [CHAR LIMIT=NONE] --> <string name="notification_channel_screenshot">Screenshots</string> - <!-- Title for the notification channel for miscellaneous notices. [CHAR LIMIT=NONE] --> - <string name="notification_channel_general">General Messages</string> + <!-- Title for the notification channel for instant app notices. [CHAR LIMIT=NONE] --> + <string name="notification_channel_instant">Instant Apps</string> + <!-- Title for the notification channel for setup notices. [CHAR LIMIT=NONE] --> + <string name="notification_channel_setup">Setup</string> <!-- Title for the notification channel for problems with storage (i.e. low disk). [CHAR LIMIT=NONE] --> <string name="notification_channel_storage">Storage</string> <!-- Title for the notification channel for hints and suggestions. [CHAR LIMIT=NONE] --> @@ -2121,10 +2136,10 @@ <!-- Controls management providers screen title [CHAR LIMIT=60]--> <string name="controls_providers_title">Choose app to add controls</string> <!-- Number of favorites for controls management screen [CHAR LIMIT=NONE]--> - <plurals name="controls_number_of_favorites"> - <item quantity="one"><xliff:g id="number" example="1">%s</xliff:g> control added.</item> - <item quantity="other"><xliff:g id="number" example="3">%s</xliff:g> controls added.</item> - </plurals> + <string name="controls_number_of_favorites">{count, plural, + =1 {# control added.} + other {# controls added.} + }</string> <!-- Removed control in management screen [CHAR LIMIT=20] --> <string name="controls_removed">Removed</string> @@ -2480,10 +2495,10 @@ <string name="qs_user_switch_dialog_title">Select user</string> <!-- Label for the entry point to open the dialog which shows currently running applications [CHAR LIMIT=NONE]--> - <plurals name="fgs_manager_footer_label"> - <item quantity="one"><xliff:g id="count" example="1">%s</xliff:g> app is active</item> - <item quantity="other"><xliff:g id="count" example="2">%s</xliff:g> apps are active</item> - </plurals> + <string name="fgs_manager_footer_label">{count, plural, + =1 {# app is active} + other {# apps are active} + }</string> <!-- Content description for a dot indicator in the running application indicating that there is new information [CHAR LIMIT=NONE] --> <string name="fgs_dot_content_description">New information</string> @@ -2544,4 +2559,18 @@ =1 {# notification} other {# notifications} }</string> + + <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting --> + <string name="broadcasting_description_is_broadcasting">Broadcasting</string> + <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, title --> + <string name="bt_le_audio_broadcast_dialog_title">Stop broadcasting <xliff:g id="app_name" example="App Name 1">%1$s</xliff:g>?</string> + <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, sub-title --> + <string name="bt_le_audio_broadcast_dialog_sub_title">If you broadcast <xliff:g id="switchApp" example="App Name 2">%1$s</xliff:g> or change the output, your current broadcast will stop</string> + <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, switch to others app. --> + <string name="bt_le_audio_broadcast_dialog_switch_app">Broadcast <xliff:g id="switchApp" example="App Name 2">%1$s</xliff:g></string> + <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, different output. --> + <string name="bt_le_audio_broadcast_dialog_different_output">Change output</string> + <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is unknown --> + <string name="bt_le_audio_broadcast_dialog_unknown_name">Unknown</string> + </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 0c25f5473416..8b2481cff2a9 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -1196,4 +1196,48 @@ <item name="android:shadowColor">@color/keyguard_shadow_color</item> <item name="android:shadowRadius">?attr/shadowRadius</item> </style> + + <style name="BroadcastDialogTitleStyle"> + <item name="android:textAppearance">@style/TextAppearanceBroadcastDialogTitle</item> + <item name="android:layout_marginStart">@dimen/broadcast_dialog_title_text_margin</item> + <item name="android:layout_marginEnd">@dimen/broadcast_dialog_title_text_margin</item> + <item name="android:layout_marginTop">@dimen/broadcast_dialog_title_text_margin_top</item> + <item name="android:layout_marginBottom">18dp</item> + </style> + + <style name="TextAppearanceBroadcastDialogTitle" parent="@android:style/TextAppearance.DeviceDefault.Headline"> + <item name="android:textSize">@dimen/broadcast_dialog_title_text_size</item> + <item name="android:textColor">?android:attr/textColorPrimary</item> + <item name="android:textDirection">locale</item> + <item name="android:ellipsize">end</item> + </style> + + <style name="BroadcastDialogBodyStyle"> + <item name="android:textAppearance">@style/TextAppearanceBroadcastDialogSubTitle</item> + <item name="android:layout_margin">@dimen/broadcast_dialog_title_text_margin</item> + </style> + + <style name="TextAppearanceBroadcastDialogSubTitle" parent="@android:style/TextAppearance.DeviceDefault.Headline"> + <item name="android:textSize">@dimen/broadcast_dialog_subtitle_text_size</item> + <item name="android:textColor">?android:attr/textColorSecondary</item> + <item name="android:textDirection">locale</item> + <item name="android:ellipsize">end</item> + </style> + + <style name="BroadcastDialogButtonStyle"> + <item name="android:textAppearance">@style/TextAppearanceBroadcastDialogButton</item> + <item name="android:layout_width">match_parent</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_gravity">center</item> + <item name="android:gravity">center</item> + <item name="android:stateListAnimator">@null</item> + <item name="android:elevation">0dp</item> + <item name="android:minHeight">@dimen/broadcast_dialog_btn_minHeight</item> + <item name="android:background">@drawable/broadcast_dialog_btn_bg</item> + </style> + + <style name="TextAppearanceBroadcastDialogButton" parent="@android:style/TextAppearance.DeviceDefault.Headline"> + <item name="android:textColor">?androidprv:attr/textColorOnAccent</item> + <item name="android:textSize">@dimen/broadcast_dialog_btn_text_size</item> + </style> </resources> diff --git a/packages/SystemUI/res/values/tiles_states_strings.xml b/packages/SystemUI/res/values/tiles_states_strings.xml index e2734164161f..c8095510e2c2 100644 --- a/packages/SystemUI/res/values/tiles_states_strings.xml +++ b/packages/SystemUI/res/values/tiles_states_strings.xml @@ -308,4 +308,14 @@ <item>Off</item> <item>On</item> </string-array> + + <!-- State names for dream (screensaver) tile: unavailable, off, on. + This subtitle is shown when the tile is in that particular state but does not set its own + subtitle, so some of these may never appear on screen. They should still be translated as + if they could appear. [CHAR LIMIT=32] --> + <string-array name="tile_states_dream"> + <item>Unavailable</item> + <item>Off</item> + <item>On</item> + </string-array> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 3cf5bc1bf13a..9f790c66302d 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -47,6 +47,7 @@ android_library { ], static_libs: [ "PluginCoreLib", + "SystemUIUnfoldLib", "androidx.dynamicanimation_dynamicanimation", "androidx.concurrent_concurrent-futures", "dagger2", diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java index 675dc9b533fb..e9c1acbf7ca0 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java @@ -243,7 +243,7 @@ public class Task { return new Task(taskKey, td != null ? td.getPrimaryColor() : 0, td != null ? td.getBackgroundColor() : 0, - taskInfo.supportsSplitScreenMultiWindow, isLocked, td, taskInfo.topActivity); + taskInfo.supportsMultiWindow, isLocked, td, taskInfo.topActivity); } public Task(TaskKey key) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java index 01f417f147d7..b29dc835a6f4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java @@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.FEATURE_PC; import static android.view.Display.DEFAULT_DISPLAY; import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION; +import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -97,6 +98,7 @@ public class RotationButtonController { @SuppressLint("InlinedApi") private @WindowInsetsController.Behavior int mBehavior = WindowInsetsController.BEHAVIOR_DEFAULT; + private int mNavBarMode; private boolean mSkipOverrideUserLockPrefsOnce; private final int mLightIconColor; private final int mDarkIconColor; @@ -397,6 +399,10 @@ public class RotationButtonController { if (rotateSuggestionsDisabled) onRotationSuggestionsDisabled(); } + public void onNavigationModeChanged(int mode) { + mNavBarMode = mode; + } + public void onBehaviorChanged(int displayId, @WindowInsetsController.Behavior int behavior) { if (DEFAULT_DISPLAY != displayId) { return; @@ -433,7 +439,8 @@ public class RotationButtonController { */ @SuppressLint("InlinedApi") private boolean canShowRotationButton() { - return mIsNavigationBarShowing || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT; + return mIsNavigationBarShowing || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT + || isGesturalMode(mNavBarMode); } @DrawableRes diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java index 618d2d2f213a..76a09b38036c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationAdapterCompat.java @@ -137,6 +137,8 @@ public class RemoteAnimationAdapterCompat { float displayH = 0; for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); + // skip changes that we didn't wrap + if (!leashMap.containsKey(change.getLeash())) continue; if (change.getTaskInfo() != null && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_HOME) { isReturnToHome = change.getMode() == TRANSIT_OPEN @@ -173,6 +175,8 @@ public class RemoteAnimationAdapterCompat { for (int i = info.getChanges().size() - 1; i >= 0; --i) { final TransitionInfo.Change change = info.getChanges().get(i); final SurfaceControl leash = leashMap.get(change.getLeash()); + // skip changes that we didn't wrap + if (leash == null) continue; final int mode = info.getChanges().get(i).getMode(); // Only deal with independent layers if (!TransitionInfo.isIndependent(change, info)) continue; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java index 9cf482fdf790..4ce110bf7c47 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java @@ -17,7 +17,6 @@ package com.android.systemui.shared.system; import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; -import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; @@ -32,7 +31,6 @@ import android.app.WindowConfiguration; import android.graphics.Point; import android.graphics.Rect; import android.util.ArrayMap; -import android.util.IntArray; import android.util.SparseArray; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; @@ -142,21 +140,10 @@ public class RemoteAnimationTargetCompat { // changes should be ordered top-to-bottom in z final int mode = change.getMode(); - // Don't move anything that isn't independent within its parents - if (!TransitionInfo.isIndependent(change, info)) { - if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) { - t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y); - } - return; - } - - final boolean hasParent = change.getParent() != null; + t.reparent(leash, info.getRootLeash()); + t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x, + change.getStartAbsBounds().top - info.getRootOffset().y); - if (!hasParent) { - t.reparent(leash, info.getRootLeash()); - t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x, - change.getStartAbsBounds().top - info.getRootOffset().y); - } // Put all the OPEN/SHOW on top if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) { if (isOpening) { @@ -196,8 +183,7 @@ public class RemoteAnimationTargetCompat { .setContainerLayer() // Initial the surface visible to respect the visibility of the original surface. .setHidden(false) - .setParent(change.getParent() == null ? info.getRootLeash() - : info.getChange(change.getParent()).getLeash()) + .setParent(info.getRootLeash()) .build(); // Copied Transitions setup code (which expects bottom-to-top order, so we swap here) setupLeash(leashSurface, change, info.getChanges().size() - order, info, t); @@ -269,58 +255,32 @@ public class RemoteAnimationTargetCompat { public static RemoteAnimationTargetCompat[] wrap(TransitionInfo info, boolean wallpapers, SurfaceControl.Transaction t, ArrayMap<SurfaceControl, SurfaceControl> leashMap) { final ArrayList<RemoteAnimationTargetCompat> out = new ArrayList<>(); - final SparseArray<RemoteAnimationTargetCompat> childTaskTargets = new SparseArray<>(); - final IntArray excludedParentTaskIds = new IntArray(); + final SparseArray<TransitionInfo.Change> childTaskTargets = new SparseArray<>(); for (int i = 0; i < info.getChanges().size(); i++) { final TransitionInfo.Change change = info.getChanges().get(i); final boolean changeIsWallpaper = (change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0; if (wallpapers != changeIsWallpaper) continue; - final RemoteAnimationTargetCompat targetCompat = - new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t); - if (leashMap != null) { - leashMap.put(change.getLeash(), targetCompat.leash); - } - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo != null) { - // Skip wrapping excluded parent task animate target since it will animate its child - // tasks instead. - if (excludedParentTaskIds.binarySearch(taskInfo.taskId) != -1) { - continue; - } - - // Check if there's a matching child task target in cache. - RemoteAnimationTargetCompat childTaskTarget = childTaskTargets.get(taskInfo.taskId); - if (childTaskTarget != null) { - // Launcher monitors leaf task ids to perform animation, override the target - // with its child task information so Launcher can animate this parent surface - // directly with leaf task information. - targetCompat.taskInfo = childTaskTarget.taskInfo; - targetCompat.taskId = childTaskTarget.taskId; - childTaskTargets.remove(taskInfo.taskId); - } - - // Check if it has a parent task, cache its information for later use. - if (taskInfo.parentTaskId != -1 - && excludedParentTaskIds.binarySearch(taskInfo.parentTaskId) == -1) { - if (!childTaskTargets.contains(taskInfo.parentTaskId)) { - // Cache the target amd skip wrapping it info the final animation targets. - // Otherwise, the child task might get transformed multiple-times with the - // flow like RecentsView#redrawLiveTile. - childTaskTargets.put(taskInfo.parentTaskId, targetCompat); + if (!wallpapers) { + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); + // Children always come before parent since changes are in top-to-bottom z-order. + if (taskInfo != null) { + if (childTaskTargets.contains(taskInfo.taskId)) { + // has children, so not a leaf. Skip. continue; } - - // There is another child task target cached with the same parent task id. - // Which means the parent having multiple child tasks in transition. Stop - // propagate child task info. - childTaskTarget = childTaskTargets.removeReturnOld(taskInfo.parentTaskId); - out.add(childTaskTarget); - excludedParentTaskIds.add(taskInfo.parentTaskId); + if (taskInfo.hasParentTask()) { + childTaskTargets.put(taskInfo.parentTaskId, change); + } } } + final RemoteAnimationTargetCompat targetCompat = + new RemoteAnimationTargetCompat(change, info.getChanges().size() - i, info, t); + if (leashMap != null) { + leashMap.put(change.getLeash(), targetCompat.leash); + } out.add(targetCompat); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java index de35514be2cb..e9ac26182c7e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java @@ -21,12 +21,14 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; +import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionFilter.CONTAINER_ORDER_TOP; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_RECENTS; +import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING; import android.annotation.NonNull; import android.annotation.Nullable; @@ -132,16 +134,17 @@ public class RemoteTransitionCompat implements Parcelable { // TODO(b/177438007): Move this set-up logic into launcher's animation impl. mToken = transition; // This transition is for opening recents, so recents is on-top. We want to draw - // the current going-away task on top of recents, though, so move it to front + // the current going-away tasks on top of recents, though, so move them to front. + // Note that we divide up the "layer space" into 3 regions each the size of + // the change count. This way we can easily move changes into above/below/between + // while maintaining their relative ordering. final ArrayList<WindowContainerToken> pausingTasks = new ArrayList<>(); WindowContainerToken pipTask = null; WindowContainerToken recentsTask = null; - for (int i = info.getChanges().size() - 1; i >= 0; --i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (change.getMode() == TRANSIT_CLOSE || change.getMode() == TRANSIT_TO_BACK) { - t.setLayer(leashMap.get(change.getLeash()), - info.getChanges().size() * 3 - i); + for (int i = apps.length - 1; i >= 0; --i) { + final ActivityManager.RunningTaskInfo taskInfo = apps[i].taskInfo; + if (apps[i].mode == MODE_CLOSING) { + t.setLayer(apps[i].leash, info.getChanges().size() * 3 - i); if (taskInfo == null) { continue; } @@ -154,8 +157,7 @@ public class RemoteTransitionCompat implements Parcelable { } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) { // This task is for recents, keep it on top. - t.setLayer(leashMap.get(change.getLeash()), - info.getChanges().size() * 3 - i); + t.setLayer(apps[i].leash, info.getChanges().size() * 3 - i); recentsTask = taskInfo.token; } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { recentsTask = taskInfo.token; @@ -167,7 +169,8 @@ public class RemoteTransitionCompat implements Parcelable { } t.apply(); mRecentsSession.setup(controller, info, finishedCallback, pausingTasks, pipTask, - recentsTask, leashMap, mToken); + recentsTask, leashMap, mToken, + (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0); recents.onAnimationStart(mRecentsSession, apps, wallpapers, new Rect(0, 0, 0, 0), new Rect()); } @@ -222,12 +225,13 @@ public class RemoteTransitionCompat implements Parcelable { private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; private PictureInPictureSurfaceTransaction mPipTransaction = null; private IBinder mTransition = null; + private boolean mKeyguardLocked = false; void setup(RecentsAnimationControllerCompat wrapped, TransitionInfo info, IRemoteTransitionFinishedCallback finishCB, ArrayList<WindowContainerToken> pausingTasks, WindowContainerToken pipTask, WindowContainerToken recentsTask, ArrayMap<SurfaceControl, SurfaceControl> leashMap, - IBinder transition) { + IBinder transition, boolean keyguardLocked) { if (mInfo != null) { throw new IllegalStateException("Trying to run a new recents animation while" + " recents is already active."); @@ -240,6 +244,7 @@ public class RemoteTransitionCompat implements Parcelable { mRecentsTask = recentsTask; mLeashMap = leashMap; mTransition = transition; + mKeyguardLocked = keyguardLocked; } @SuppressLint("NewApi") @@ -374,6 +379,10 @@ public class RemoteTransitionCompat implements Parcelable { final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); final WindowContainerTransaction wct = new WindowContainerTransaction(); + if (mKeyguardLocked && mRecentsTask != null) { + if (toHome) wct.reorder(mRecentsTask, true /* toTop */); + else wct.restoreTransientOrder(mRecentsTask); + } if (!toHome && mPausingTasks != null && mOpeningLeashes == null) { // The gesture went back to opening the app rather than continuing with // recents, so end the transition by moving the app back to the top (and also @@ -383,16 +392,21 @@ public class RemoteTransitionCompat implements Parcelable { wct.reorder(mPausingTasks.get(i), true /* onTop */); t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash()); } - if (mRecentsTask != null) { + if (!mKeyguardLocked && mRecentsTask != null) { wct.restoreTransientOrder(mRecentsTask); } } else { - if (!sendUserLeaveHint) { - for (int i = 0; i < mPausingTasks.size(); ++i) { + for (int i = 0; i < mPausingTasks.size(); ++i) { + if (!sendUserLeaveHint) { // This means recents is not *actually* finishing, so of course we gotta // do special stuff in WMCore to accommodate. wct.setDoNotPip(mPausingTasks.get(i)); } + // Since we will reparent out of the leashes, pre-emptively hide the child + // surface to match the leash. Otherwise, there will be a flicker before the + // visibility gets committed in Core when using split-screen (in splitscreen, + // the leaf-tasks are not "independent" so aren't hidden by normal setup). + t.hide(mInfo.getChange(mPausingTasks.get(i)).getLeash()); } if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) { t.show(mInfo.getChange(mPipTask).getLeash()); diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt deleted file mode 100644 index d1b06394b818..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2021 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.unfold.config - -import android.content.Context -import android.os.SystemProperties - -internal class ResourceUnfoldTransitionConfig(private val context: Context) : - UnfoldTransitionConfig { - - override val isEnabled: Boolean - get() = readIsEnabledResource() && isPropertyEnabled - - override val isHingeAngleEnabled: Boolean - get() = readIsHingeAngleEnabled() - - private val isPropertyEnabled: Boolean - get() = - SystemProperties.getInt( - UNFOLD_TRANSITION_MODE_PROPERTY_NAME, UNFOLD_TRANSITION_PROPERTY_ENABLED) == - UNFOLD_TRANSITION_PROPERTY_ENABLED - - private fun readIsEnabledResource(): Boolean = - context.resources.getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled) - - private fun readIsHingeAngleEnabled(): Boolean = - context.resources.getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle) -} - -/** - * Temporary persistent property to control unfold transition mode. - * - * See [com.android.unfold.config.AnimationMode]. - */ -private const val UNFOLD_TRANSITION_MODE_PROPERTY_NAME = "persist.unfold.transition_enabled" -private const val UNFOLD_TRANSITION_PROPERTY_ENABLED = 1 diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt new file mode 100644 index 000000000000..7f2933e44b32 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 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.unfold.system + +import android.app.ActivityManager +import android.app.WindowConfiguration +import com.android.systemui.unfold.util.CurrentActivityTypeProvider +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ActivityManagerActivityTypeProvider @Inject constructor( + private val activityManager: ActivityManager +) : CurrentActivityTypeProvider { + + override val isHomeActivity: Boolean? + get() { + val activityType = activityManager.getRunningTasks(/* maxNum= */ 1) + ?.getOrNull(0)?.topActivityType ?: return null + + return activityType == WindowConfiguration.ACTIVITY_TYPE_HOME + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt new file mode 100644 index 000000000000..3b8d318a3a79 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 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.unfold.system + +import android.content.Context +import android.hardware.devicestate.DeviceStateManager +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.updates.FoldProvider.FoldCallback +import java.util.concurrent.Executor +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DeviceStateManagerFoldProvider @Inject constructor( + private val deviceStateManager: DeviceStateManager, + private val context: Context +) : FoldProvider { + + private val callbacks: MutableMap<FoldCallback, + DeviceStateManager.DeviceStateCallback> = hashMapOf() + + override fun registerCallback(callback: FoldCallback, executor: Executor) { + val listener = FoldStateListener(context, callback) + deviceStateManager.registerCallback(executor, listener) + callbacks[callback] = listener + } + + override fun unregisterCallback(callback: FoldCallback) { + val listener = callbacks.remove(callback) + listener?.let { + deviceStateManager.unregisterCallback(it) + } + } + + private inner class FoldStateListener( + context: Context, + listener: FoldCallback + ) : DeviceStateManager.FoldStateListener(context, { listener.onFoldUpdated(it) }) +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt new file mode 100644 index 000000000000..24ae42ae4db2 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2022 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.unfold.system + +import android.os.Handler +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dagger.qualifiers.UiBackground +import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig +import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.dagger.UnfoldBackground +import com.android.systemui.unfold.dagger.UnfoldMain +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.util.CurrentActivityTypeProvider +import dagger.Binds +import dagger.Module +import java.util.concurrent.Executor + +/** + * Dagger module with system-only dependencies for the unfold animation. + * The code that is used to calculate unfold transition progress + * depends on some hidden APIs that are not available in normal + * apps. In order to re-use this code and use alternative implementations + * of these classes in other apps and hidden APIs here. + */ +@Module +abstract class SystemUnfoldSharedModule { + + @Binds + abstract fun activityTypeProvider(executor: ActivityManagerActivityTypeProvider): + CurrentActivityTypeProvider + + @Binds + abstract fun config(config: ResourceUnfoldTransitionConfig): UnfoldTransitionConfig + + @Binds + abstract fun foldState(provider: DeviceStateManagerFoldProvider): FoldProvider + + @Binds + @UnfoldMain + abstract fun mainExecutor(@Main executor: Executor): Executor + + @Binds + @UnfoldMain + abstract fun mainHandler(@Main handler: Handler): Handler + + @Binds + @UnfoldBackground + abstract fun backgroundExecutor(@UiBackground executor: Executor): Executor +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt deleted file mode 100644 index b351585de364..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.android.systemui.unfold.updates.hinge - -import androidx.core.util.Consumer - -internal object EmptyHingeAngleProvider : HingeAngleProvider { - override fun start() {} - - override fun stop() {} - - override fun removeCallback(listener: Consumer<Float>) {} - - override fun addCallback(listener: Consumer<Float>) {} -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt deleted file mode 100644 index 48a5b12c759a..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.android.systemui.unfold.updates.hinge - -import androidx.core.util.Consumer -import com.android.systemui.statusbar.policy.CallbackController - -/** - * Emits device hinge angle values (angle between two integral parts of the device). - * - * The hinge angle could be from 0 to 360 degrees inclusive. For foldable devices usually 0 - * corresponds to fully closed (folded) state and 180 degrees corresponds to fully open (flat) - * state. - */ -interface HingeAngleProvider : CallbackController<Consumer<Float>> { - fun start() - fun stop() -} - -const val FULLY_OPEN_DEGREES = 180f -const val FULLY_CLOSED_DEGREES = 0f diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt index 53c528ff24a8..ec938b219933 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProvider.kt @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2022 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.unfold.util import android.content.Context diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java index 47df70b522f7..2f6fa145664e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java @@ -16,6 +16,8 @@ package com.android.keyguard; +import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; + import android.annotation.NonNull; import android.app.AlertDialog; import android.app.AlertDialog.Builder; @@ -241,10 +243,9 @@ public class KeyguardSimPinViewController displayMessage = mView.getResources().getString( R.string.kg_password_wrong_pin_code_pukked); } else if (attemptsRemaining > 0) { - msgId = isDefault ? R.plurals.kg_password_default_pin_message : - R.plurals.kg_password_wrong_pin_code; - displayMessage = mView.getResources() - .getQuantityString(msgId, attemptsRemaining, attemptsRemaining); + msgId = isDefault ? R.string.kg_password_default_pin_message : + R.string.kg_password_wrong_pin_code; + displayMessage = icuMessageFormat(mView.getResources(), msgId, attemptsRemaining); } else { msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed; displayMessage = mView.getResources().getString(msgId); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java index 760c2ccb71b5..c0971bf8c16d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java @@ -16,6 +16,8 @@ package com.android.keyguard; +import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; + import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; @@ -57,10 +59,9 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { if (attemptsRemaining == 0) { displayMessage = getContext().getString(R.string.kg_password_wrong_puk_code_dead); } else if (attemptsRemaining > 0) { - int msgId = isDefault ? R.plurals.kg_password_default_puk_message : - R.plurals.kg_password_wrong_puk_code; - displayMessage = getContext().getResources() - .getQuantityString(msgId, attemptsRemaining, attemptsRemaining); + int msgId = isDefault ? R.string.kg_password_default_puk_message : + R.string.kg_password_wrong_puk_code; + displayMessage = icuMessageFormat(getResources(), msgId, attemptsRemaining); } else { int msgId = isDefault ? R.string.kg_puk_enter_puk_hint : R.string.kg_password_puk_failed; diff --git a/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java new file mode 100644 index 000000000000..fd84543ee50b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/GuestResetOrExitSessionReceiver.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2022 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; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.UserInfo; +import android.os.UserHandle; + +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.qs.QSUserSwitcherEvent; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.phone.SystemUIDialog; +import com.android.systemui.statusbar.policy.UserSwitcherController; + +import javax.inject.Inject; + +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; + +/** + * Manages handling of guest session persistent notification + * and actions to reset guest or exit guest session + */ +public final class GuestResetOrExitSessionReceiver extends BroadcastReceiver { + + private static final String TAG = GuestResetOrExitSessionReceiver.class.getSimpleName(); + + /** + * Broadcast sent to the system when guest user needs to be reset. + * This is only sent to registered receivers, not manifest receivers. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_GUEST_RESET = "android.intent.action.GUEST_RESET"; + + /** + * Broadcast sent to the system when guest user needs to exit. + * This is only sent to registered receivers, not manifest receivers. + * + * @hide + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_GUEST_EXIT = "android.intent.action.GUEST_EXIT"; + + public AlertDialog mExitSessionDialog; + public AlertDialog mResetSessionDialog; + private final UserTracker mUserTracker; + private final BroadcastDispatcher mBroadcastDispatcher; + private final ResetSessionDialog.Factory mResetSessionDialogFactory; + private final ExitSessionDialog.Factory mExitSessionDialogFactory; + + @Inject + public GuestResetOrExitSessionReceiver(UserTracker userTracker, + BroadcastDispatcher broadcastDispatcher, + ResetSessionDialog.Factory resetSessionDialogFactory, + ExitSessionDialog.Factory exitSessionDialogFactory) { + mUserTracker = userTracker; + mBroadcastDispatcher = broadcastDispatcher; + mResetSessionDialogFactory = resetSessionDialogFactory; + mExitSessionDialogFactory = exitSessionDialogFactory; + } + + /** + * Register this receiver with the {@link BroadcastDispatcher} + */ + public void register() { + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(ACTION_GUEST_RESET); + intentFilter.addAction(ACTION_GUEST_EXIT); + mBroadcastDispatcher.registerReceiver(this, intentFilter, null /* handler */, + UserHandle.SYSTEM); + } + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + + cancelResetDialog(); + cancelExitDialog(); + + UserInfo currentUser = mUserTracker.getUserInfo(); + if (!currentUser.isGuest()) { + return; + } + + if (ACTION_GUEST_RESET.equals(action)) { + mResetSessionDialog = mResetSessionDialogFactory.create(currentUser.id); + mResetSessionDialog.show(); + } else if (ACTION_GUEST_EXIT.equals(action)) { + mExitSessionDialog = mExitSessionDialogFactory.create(currentUser.id, + currentUser.isEphemeral()); + mExitSessionDialog.show(); + } + } + + private void cancelResetDialog() { + if (mResetSessionDialog != null && mResetSessionDialog.isShowing()) { + mResetSessionDialog.cancel(); + mResetSessionDialog = null; + } + } + + private void cancelExitDialog() { + if (mExitSessionDialog != null && mExitSessionDialog.isShowing()) { + mExitSessionDialog.cancel(); + mExitSessionDialog = null; + } + } + + /** + * Dialog shown when asking for confirmation before + * reset and restart of guest user. + */ + public static final class ResetSessionDialog extends SystemUIDialog implements + DialogInterface.OnClickListener { + + private final UserSwitcherController mUserSwitcherController; + private final UiEventLogger mUiEventLogger; + private final int mUserId; + + /** Factory class to create guest reset dialog instance */ + @AssistedFactory + public interface Factory { + /** Create a guest reset dialog instance */ + ResetSessionDialog create(int userId); + } + + @AssistedInject + ResetSessionDialog(Context context, + UserSwitcherController userSwitcherController, + UiEventLogger uiEventLogger, + @Assisted int userId) { + super(context); + + setTitle(com.android.settingslib.R.string.guest_reset_and_restart_dialog_title); + setMessage(context.getString( + com.android.settingslib.R.string.guest_reset_and_restart_dialog_message)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString( + com.android.settingslib.R.string.guest_reset_guest_confirm_button), this); + setCanceledOnTouchOutside(false); + + mUserSwitcherController = userSwitcherController; + mUiEventLogger = uiEventLogger; + mUserId = userId; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (which == DialogInterface.BUTTON_POSITIVE) { + mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); + mUserSwitcherController.removeGuestUser(mUserId, UserHandle.USER_NULL); + } else if (which == DialogInterface.BUTTON_NEUTRAL) { + cancel(); + } + } + } + + /** + * Dialog shown when asking for confirmation before + * exit of guest user. + */ + public static final class ExitSessionDialog extends SystemUIDialog implements + DialogInterface.OnClickListener { + + private final UserSwitcherController mUserSwitcherController; + private final int mUserId; + private boolean mIsEphemeral; + + /** Factory class to create guest exit dialog instance */ + @AssistedFactory + public interface Factory { + /** Create a guest exit dialog instance */ + ExitSessionDialog create(int userId, boolean isEphemeral); + } + + @AssistedInject + ExitSessionDialog(Context context, + UserSwitcherController userSwitcherController, + @Assisted int userId, + @Assisted boolean isEphemeral) { + super(context); + + if (isEphemeral) { + setTitle(context.getString( + com.android.settingslib.R.string.guest_exit_dialog_title)); + setMessage(context.getString( + com.android.settingslib.R.string.guest_exit_dialog_message)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_dialog_button), this); + } else { + setTitle(context.getString( + com.android.settingslib + .R.string.guest_exit_dialog_title_non_ephemeral)); + setMessage(context.getString( + com.android.settingslib + .R.string.guest_exit_dialog_message_non_ephemeral)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_NEGATIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_clear_data_button), this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_save_data_button), this); + } + setCanceledOnTouchOutside(false); + + mUserSwitcherController = userSwitcherController; + mUserId = userId; + mIsEphemeral = isEphemeral; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + if (mIsEphemeral) { + if (which == DialogInterface.BUTTON_POSITIVE) { + // Ephemeral guest: exit guest, guest is removed by the system + // on exit, since its marked ephemeral + mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false); + } else if (which == DialogInterface.BUTTON_NEUTRAL) { + // Cancel clicked, do nothing + cancel(); + } + } else { + if (which == DialogInterface.BUTTON_POSITIVE) { + // Non-ephemeral guest: exit guest, guest is not removed by the system + // on exit, since its marked non-ephemeral + mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, false); + } else if (which == DialogInterface.BUTTON_NEGATIVE) { + // Non-ephemeral guest: remove guest and then exit + mUserSwitcherController.exitGuestUser(mUserId, UserHandle.USER_NULL, true); + } else if (which == DialogInterface.BUTTON_NEUTRAL) { + // Cancel clicked, do nothing + cancel(); + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java index 9a6020f8556b..76a7cad15419 100644 --- a/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/GuestResumeSessionReceiver.java @@ -35,12 +35,18 @@ import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.settings.SecureSettings; +import javax.inject.Inject; + +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; + /** * Manages notification when a guest session is resumed. */ public class GuestResumeSessionReceiver extends BroadcastReceiver { - private static final String TAG = "GuestResumeSessionReceiver"; + private static final String TAG = GuestResumeSessionReceiver.class.getSimpleName(); @VisibleForTesting public static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in"; @@ -48,27 +54,31 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { @VisibleForTesting public AlertDialog mNewSessionDialog; private final UserTracker mUserTracker; - private final UserSwitcherController mUserSwitcherController; - private final UiEventLogger mUiEventLogger; private final SecureSettings mSecureSettings; - - public GuestResumeSessionReceiver(UserSwitcherController userSwitcherController, - UserTracker userTracker, UiEventLogger uiEventLogger, - SecureSettings secureSettings) { - mUserSwitcherController = userSwitcherController; + private final BroadcastDispatcher mBroadcastDispatcher; + private final ResetSessionDialog.Factory mResetSessionDialogFactory; + private final GuestSessionNotification mGuestSessionNotification; + + @Inject + public GuestResumeSessionReceiver( + UserTracker userTracker, + SecureSettings secureSettings, + BroadcastDispatcher broadcastDispatcher, + GuestSessionNotification guestSessionNotification, + ResetSessionDialog.Factory resetSessionDialogFactory) { mUserTracker = userTracker; - mUiEventLogger = uiEventLogger; mSecureSettings = secureSettings; + mBroadcastDispatcher = broadcastDispatcher; + mGuestSessionNotification = guestSessionNotification; + mResetSessionDialogFactory = resetSessionDialogFactory; } /** * Register this receiver with the {@link BroadcastDispatcher} - * - * @param broadcastDispatcher to register the receiver. */ - public void register(BroadcastDispatcher broadcastDispatcher) { + public void register() { IntentFilter f = new IntentFilter(Intent.ACTION_USER_SWITCHED); - broadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM); + mBroadcastDispatcher.registerReceiver(this, f, null /* handler */, UserHandle.SYSTEM); } @Override @@ -89,14 +99,25 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { return; } - int notFirstLogin = mSecureSettings.getIntForUser( + int guestLoginState = mSecureSettings.getIntForUser( SETTING_GUEST_HAS_LOGGED_IN, 0, userId); - if (notFirstLogin != 0) { - mNewSessionDialog = new ResetSessionDialog(context, mUserSwitcherController, - mUiEventLogger, userId); + + if (guestLoginState == 0) { + // set 1 to indicate, 1st login + guestLoginState = 1; + mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId); + } else if (guestLoginState == 1) { + // set 2 to indicate, 2nd or later login + guestLoginState = 2; + mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, guestLoginState, userId); + } + + mGuestSessionNotification.createPersistentNotification(currentUser, + (guestLoginState <= 1)); + + if (guestLoginState > 1) { + mNewSessionDialog = mResetSessionDialogFactory.create(userId); mNewSessionDialog.show(); - } else { - mSecureSettings.putIntForUser(SETTING_GUEST_HAS_LOGGED_IN, 1, userId); } } } @@ -124,10 +145,19 @@ public class GuestResumeSessionReceiver extends BroadcastReceiver { private final UiEventLogger mUiEventLogger; private final int mUserId; - ResetSessionDialog(Context context, + + /** Factory class to create guest reset dialog instance */ + @AssistedFactory + public interface Factory { + /** Create a guest reset dialog instance */ + ResetSessionDialog create(int userId); + } + + @AssistedInject + public ResetSessionDialog(Context context, UserSwitcherController userSwitcherController, UiEventLogger uiEventLogger, - int userId) { + @Assisted int userId) { super(context, false /* dismissOnDeviceLock */); setTitle(context.getString(R.string.guest_wipe_session_title)); diff --git a/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java new file mode 100644 index 000000000000..b0eaab97c5ac --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/GuestSessionNotification.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2022 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; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.content.pm.UserInfo; +import android.os.Bundle; +import android.os.UserHandle; +import android.provider.Settings; +import android.util.FeatureFlagUtils; + +import com.android.internal.messages.nano.SystemMessageProto; +import com.android.systemui.util.NotificationChannels; + +import javax.inject.Inject; + +/** + * Posts a persistent notification on entry to guest mode + */ +public final class GuestSessionNotification { + + private static final String TAG = GuestSessionNotification.class.getSimpleName(); + + private final Context mContext; + private final NotificationManager mNotificationManager; + + @Inject + public GuestSessionNotification(Context context, + NotificationManager notificationManager) { + mContext = context; + mNotificationManager = notificationManager; + } + + private void overrideNotificationAppName(Notification.Builder notificationBuilder) { + final Bundle extras = new Bundle(); + String appName = mContext.getString(R.string.guest_notification_app_name); + + extras.putString(Notification.EXTRA_SUBSTITUTE_APP_NAME, appName); + + notificationBuilder.addExtras(extras); + } + + void createPersistentNotification(UserInfo userInfo, boolean isGuestFirstLogin) { + if (!FeatureFlagUtils.isEnabled(mContext, + FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES) + || !userInfo.isGuest()) { + // we create a persistent notification only if enabled and only for guests + return; + } + String contentText; + if (userInfo.isEphemeral()) { + contentText = mContext.getString(R.string.guest_notification_ephemeral); + } else if (isGuestFirstLogin) { + contentText = mContext.getString(R.string.guest_notification_non_ephemeral); + } else { + contentText = mContext.getString( + R.string.guest_notification_non_ephemeral_non_first_login); + } + + final Intent guestExitIntent = new Intent( + GuestResetOrExitSessionReceiver.ACTION_GUEST_EXIT); + final Intent userSettingsIntent = new Intent(Settings.ACTION_USER_SETTINGS); + + PendingIntent guestExitPendingIntent = + PendingIntent.getBroadcastAsUser(mContext, 0, guestExitIntent, + PendingIntent.FLAG_IMMUTABLE, + UserHandle.SYSTEM); + + PendingIntent userSettingsPendingIntent = + PendingIntent.getActivityAsUser(mContext, 0, userSettingsIntent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE, + null, + UserHandle.of(userInfo.id)); + + Notification.Builder builder = new Notification.Builder(mContext, + NotificationChannels.ALERTS) + .setSmallIcon(R.drawable.ic_account_circle) + .setContentTitle(mContext.getString(R.string.guest_notification_session_active)) + .setContentText(contentText) + .setPriority(Notification.PRIORITY_DEFAULT) + .setOngoing(true) + .setContentIntent(userSettingsPendingIntent); + + // we show reset button only if this is a 2nd or later login + if (!isGuestFirstLogin) { + final Intent guestResetIntent = new Intent( + GuestResetOrExitSessionReceiver.ACTION_GUEST_RESET); + + PendingIntent guestResetPendingIntent = + PendingIntent.getBroadcastAsUser(mContext, 0, guestResetIntent, + PendingIntent.FLAG_IMMUTABLE, + UserHandle.SYSTEM); + + builder.addAction(R.drawable.ic_sysbar_home, + mContext.getString( + com.android.settingslib.R.string.guest_reset_guest_confirm_button), + guestResetPendingIntent); + } + builder.addAction(R.drawable.ic_sysbar_home, + mContext.getString( + com.android.settingslib.R.string.guest_exit_button), + guestExitPendingIntent); + + overrideNotificationAppName(builder); + + mNotificationManager.notifyAsUser(null, + SystemMessageProto.SystemMessage.NOTE_GUEST_SESSION, + builder.build(), + UserHandle.of(userInfo.id)); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index f5084f5cff76..2dade21caca7 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -29,9 +29,9 @@ import com.android.systemui.dagger.DaggerGlobalRootComponent; import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dagger.WMComponent; -import com.android.wm.shell.dagger.WMShellConcurrencyModule; import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider; import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider; +import com.android.wm.shell.dagger.WMShellConcurrencyModule; import com.android.wm.shell.transition.ShellTransitions; import java.util.Map; @@ -114,13 +114,11 @@ public class SystemUIFactory { // components that shouldn't be run in the test environment builder = prepareSysUIComponentBuilder(builder, mWMComponent) .setPip(mWMComponent.getPip()) - .setLegacySplitScreen(mWMComponent.getLegacySplitScreen()) .setSplitScreen(mWMComponent.getSplitScreen()) .setOneHanded(mWMComponent.getOneHanded()) .setBubbles(mWMComponent.getBubbles()) .setHideDisplayCutout(mWMComponent.getHideDisplayCutout()) .setShellCommandHandler(mWMComponent.getShellCommandHandler()) - .setAppPairs(mWMComponent.getAppPairs()) .setTaskViewFactory(mWMComponent.getTaskViewFactory()) .setTransitions(mWMComponent.getTransitions()) .setStartingSurface(mWMComponent.getStartingSurface()) @@ -135,13 +133,11 @@ public class SystemUIFactory { // is separating this logic into newly creating SystemUITestsFactory. builder = prepareSysUIComponentBuilder(builder, mWMComponent) .setPip(Optional.ofNullable(null)) - .setLegacySplitScreen(Optional.ofNullable(null)) .setSplitScreen(Optional.ofNullable(null)) .setOneHanded(Optional.ofNullable(null)) .setBubbles(Optional.ofNullable(null)) .setHideDisplayCutout(Optional.ofNullable(null)) .setShellCommandHandler(Optional.ofNullable(null)) - .setAppPairs(Optional.ofNullable(null)) .setTaskViewFactory(Optional.ofNullable(null)) .setTransitions(new ShellTransitions() {}) .setDisplayAreaHelper(Optional.ofNullable(null)) diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java index bd8e44ceab80..448b99b6e5d0 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java @@ -199,8 +199,9 @@ public class SystemActions extends CoreStartable { mNotificationShadeController = notificationShadeController; // Saving in instance variable since to prevent GC since // NotificationShadeWindowController.registerCallback() only keeps weak references. - mNotificationShadeCallback = (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing) -> - registerOrUnregisterDismissNotificationShadeAction(); + mNotificationShadeCallback = + (keyguardShowing, keyguardOccluded, bouncerShowing, mDozing, panelExpanded) -> + registerOrUnregisterDismissNotificationShadeAction(); mCentralSurfacesOptionalLazy = centralSurfacesOptionalLazy; } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java new file mode 100644 index 000000000000..9b7d49883222 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java @@ -0,0 +1,134 @@ +/** + * Copyright (C) 2022 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.bluetooth; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.TextView; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.UiEvent; +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.R; +import com.android.systemui.media.MediaDataUtils; +import com.android.systemui.media.dialog.MediaOutputDialogFactory; +import com.android.systemui.statusbar.phone.SystemUIDialog; + +/** + * Dialog for showing le audio broadcasting dialog. + */ +public class BroadcastDialog extends SystemUIDialog { + + private static final String TAG = "BroadcastDialog"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private Context mContext; + private UiEventLogger mUiEventLogger; + @VisibleForTesting + protected View mDialogView; + private MediaOutputDialogFactory mMediaOutputDialogFactory; + private String mSwitchBroadcastApp; + private String mOutputPackageName; + + public BroadcastDialog(Context context, MediaOutputDialogFactory mediaOutputDialogFactory, + String switchBroadcastApp, String outputPkgName, UiEventLogger uiEventLogger) { + super(context); + if (DEBUG) { + Log.d(TAG, "Init BroadcastDialog"); + } + + mContext = getContext(); + mMediaOutputDialogFactory = mediaOutputDialogFactory; + mSwitchBroadcastApp = switchBroadcastApp; + mOutputPackageName = outputPkgName; + mUiEventLogger = uiEventLogger; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (DEBUG) { + Log.d(TAG, "onCreate"); + } + + mUiEventLogger.log(BroadcastDialogEvent.BROADCAST_DIALOG_SHOW); + mDialogView = LayoutInflater.from(mContext).inflate(R.layout.broadcast_dialog, null); + final Window window = getWindow(); + window.setContentView(mDialogView); + + TextView title = mDialogView.requireViewById(R.id.dialog_title); + TextView subTitle = mDialogView.requireViewById(R.id.dialog_subtitle); + title.setText( + mContext.getString(R.string.bt_le_audio_broadcast_dialog_title, + MediaDataUtils.getAppLabel(mContext, mOutputPackageName, + mContext.getString( + R.string.bt_le_audio_broadcast_dialog_unknown_name)))); + subTitle.setText( + mContext.getString(R.string.bt_le_audio_broadcast_dialog_sub_title, + mSwitchBroadcastApp)); + + Button switchBroadcast = mDialogView.requireViewById(R.id.switch_broadcast); + Button changeOutput = mDialogView.requireViewById(R.id.change_output); + Button cancelBtn = mDialogView.requireViewById(R.id.cancel); + switchBroadcast.setText(mContext.getString( + R.string.bt_le_audio_broadcast_dialog_switch_app, mSwitchBroadcastApp), null); + changeOutput.setOnClickListener((view) -> { + mMediaOutputDialogFactory.create(mOutputPackageName, true, null); + dismiss(); + }); + cancelBtn.setOnClickListener((view) -> { + if (DEBUG) { + Log.d(TAG, "BroadcastDialog dismiss."); + } + dismiss(); + }); + } + + @Override + public void onWindowFocusChanged(boolean hasFocus) { + super.onWindowFocusChanged(hasFocus); + if (!hasFocus && isShowing()) { + dismiss(); + } + } + + public enum BroadcastDialogEvent implements UiEventLogger.UiEventEnum { + @UiEvent(doc = "The Broadcast dialog became visible on the screen.") + BROADCAST_DIALOG_SHOW(1062); + + private final int mId; + + BroadcastDialogEvent(int id) { + mId = id; + } + + @Override + public int getId() { + return mId; + } + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java new file mode 100644 index 000000000000..8a54345ae28c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialogController.java @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2022 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.bluetooth; + +import android.content.Context; +import android.view.View; + +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.animation.DialogLaunchAnimator; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.media.dialog.MediaOutputDialogFactory; + +import javax.inject.Inject; + +/** + * Controller to create BroadcastDialog objects. + */ +@SysUISingleton +public class BroadcastDialogController { + + private Context mContext; + private UiEventLogger mUiEventLogger; + private DialogLaunchAnimator mDialogLaunchAnimator; + private MediaOutputDialogFactory mMediaOutputDialogFactory; + + @Inject + public BroadcastDialogController(Context context, UiEventLogger uiEventLogger, + DialogLaunchAnimator dialogLaunchAnimator, + MediaOutputDialogFactory mediaOutputDialogFactory) { + mContext = context; + mUiEventLogger = uiEventLogger; + mDialogLaunchAnimator = dialogLaunchAnimator; + mMediaOutputDialogFactory = mediaOutputDialogFactory; + } + + public void createBroadcastDialog(String switchAppName, String outputPkgName, + boolean aboveStatusBar, View view) { + BroadcastDialog broadcastDialog = new BroadcastDialog(mContext, mMediaOutputDialogFactory, + switchAppName, outputPkgName, mUiEventLogger); + if (view != null) { + mDialogLaunchAnimator.showFromView(broadcastDialog, view); + } else { + broadcastDialog.show(); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt index 0bc6579739db..a174ed0312c8 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt @@ -28,6 +28,7 @@ import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.RecyclerView import com.android.systemui.R import com.android.systemui.controls.ControlsServiceInfo +import com.android.systemui.util.icuMessageFormat import java.text.Collator import java.util.concurrent.Executor @@ -74,8 +75,10 @@ class AppAdapter( } override fun onCreateViewHolder(parent: ViewGroup, i: Int): Holder { - return Holder(layoutInflater.inflate(R.layout.controls_app_item, parent, false), - favoritesRenderer) + return Holder( + layoutInflater.inflate(R.layout.controls_app_item, parent, false), + favoritesRenderer + ) } override fun getItemCount() = listOfServices.size @@ -116,10 +119,10 @@ class FavoritesRenderer( fun renderFavoritesForComponent(component: ComponentName): String? { val qty = favoriteFunction(component) - if (qty != 0) { - return resources.getQuantityString(R.plurals.controls_number_of_favorites, qty, qty) + return if (qty != 0) { + icuMessageFormat(resources, R.string.controls_number_of_favorites, qty) } else { - return null + null } } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java index 0cf3333d12a6..8ba6f1c4a411 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultBroadcastReceiverBinder.java @@ -18,6 +18,8 @@ package com.android.systemui.dagger; import android.content.BroadcastReceiver; +import com.android.systemui.GuestResetOrExitSessionReceiver; +import com.android.systemui.GuestResumeSessionReceiver; import com.android.systemui.media.dialog.MediaOutputDialogReceiver; import com.android.systemui.people.widget.PeopleSpaceWidgetPinnedReceiver; import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; @@ -89,4 +91,21 @@ public abstract class DefaultBroadcastReceiverBinder { public abstract BroadcastReceiver bindPeopleSpaceWidgetProvider( PeopleSpaceWidgetProvider broadcastReceiver); + /** + * + */ + @Binds + @IntoMap + @ClassKey(GuestResumeSessionReceiver.class) + public abstract BroadcastReceiver bindGuestResumeSessionReceiver( + GuestResumeSessionReceiver broadcastReceiver); + + /** + * + */ + @Binds + @IntoMap + @ClassKey(GuestResetOrExitSessionReceiver.class) + public abstract BroadcastReceiver bindGuestResetOrExitSessionReceiver( + GuestResetOrExitSessionReceiver broadcastReceiver); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 5d34a6987b66..3a1b12955647 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -39,14 +39,12 @@ import com.android.systemui.unfold.UnfoldLatencyTracker; import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.TaskViewFactory; -import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.draganddrop.DragAndDrop; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; @@ -86,15 +84,9 @@ public interface SysUIComponent { Builder setPip(Optional<Pip> p); @BindsInstance - Builder setLegacySplitScreen(Optional<LegacySplitScreen> s); - - @BindsInstance Builder setSplitScreen(Optional<SplitScreen> s); @BindsInstance - Builder setAppPairs(Optional<AppPairs> s); - - @BindsInstance Builder setOneHanded(Optional<OneHanded> o); @BindsInstance diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index b02074a65e9a..1570a7ebc0c4 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -26,7 +26,6 @@ import com.android.systemui.tv.TvWMComponent; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.ShellInit; import com.android.wm.shell.TaskViewFactory; -import com.android.wm.shell.apppairs.AppPairs; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.common.annotations.ShellMainThread; @@ -37,7 +36,6 @@ import com.android.wm.shell.dagger.WMSingleton; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.draganddrop.DragAndDrop; import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; -import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; @@ -96,15 +94,9 @@ public interface WMComponent { Optional<Pip> getPip(); @WMSingleton - Optional<LegacySplitScreen> getLegacySplitScreen(); - - @WMSingleton Optional<SplitScreen> getSplitScreen(); @WMSingleton - Optional<AppPairs> getAppPairs(); - - @WMSingleton Optional<Bubbles> getBubbles(); @WMSingleton diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index 19287c133651..4161cf6d2657 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -332,6 +332,20 @@ public class DozeLog implements Dumpable { } /** + * Logs the car mode started event. + */ + public void traceCarModeStarted() { + mLogger.logCarModeStarted(); + } + + /** + * Logs the car mode ended event. + */ + public void traceCarModeEnded() { + mLogger.logCarModeEnded(); + } + + /** * Appends power save changes that may cause a new doze state * @param powerSaveActive true if power saving is active * @param nextState the state that we'll transition to diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt index 4c81563e4f93..4b279ec8f008 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt @@ -105,7 +105,7 @@ class DozeLogger @Inject constructor( bool4 = screenOnFromTouch }, { "Fling expand=$bool1 aboveThreshold=$bool2 thresholdNeeded=$bool3 " + - "screenOnFromTouch=$bool4" + "screenOnFromTouch=$bool4" }) } @@ -151,7 +151,7 @@ class DozeLogger @Inject constructor( long2 = triggerAt }, { "Time tick scheduledAt=${DATE_FORMAT.format(Date(long1))} " + - "triggerAt=${DATE_FORMAT.format(Date(long2))}" + "triggerAt=${DATE_FORMAT.format(Date(long2))}" }) } @@ -220,7 +220,7 @@ class DozeLogger @Inject constructor( str1 = partUpdated }, { "Posture changed, posture=${DevicePostureController.devicePostureToString(int1)}" + - " partUpdated=$str1" + " partUpdated=$str1" }) } @@ -299,6 +299,18 @@ class DozeLogger @Inject constructor( "Doze aod dimming scrim opacity set, opacity=$long1" }) } + + fun logCarModeEnded() { + buffer.log(TAG, INFO, {}, { + "Doze car mode ended" + }) + } + + fun logCarModeStarted() { + buffer.log(TAG, INFO, {}, { + "Doze car mode started" + }) + } } private const val TAG = "DozeLog" diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java index 5779bb307790..32bc9de40f5b 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java @@ -20,6 +20,8 @@ import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWA import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; import android.annotation.MainThread; +import android.app.UiModeManager; +import android.content.res.Configuration; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Trace; import android.os.UserHandle; @@ -33,7 +35,6 @@ import com.android.systemui.doze.dagger.WrappedService; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness; import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.Assert; import com.android.systemui.util.wakelock.WakeLock; @@ -66,6 +67,8 @@ public class DozeMachine { INITIALIZED, /** Regular doze. Device is asleep and listening for pulse triggers. */ DOZE, + /** Deep doze. Device is asleep and is not listening for pulse triggers. */ + DOZE_SUSPEND_TRIGGERS, /** Always-on doze. Device is asleep, showing UI and listening for pulse triggers. */ DOZE_AOD, /** Pulse has been requested. Device is awake and preparing UI */ @@ -125,6 +128,7 @@ public class DozeMachine { : Display.STATE_ON; case DOZE_AOD_PAUSED: case DOZE: + case DOZE_SUSPEND_TRIGGERS: return Display.STATE_OFF; case DOZE_PULSING: case DOZE_PULSING_BRIGHT: @@ -143,26 +147,27 @@ public class DozeMachine { private final WakeLock mWakeLock; private final AmbientDisplayConfiguration mConfig; private final WakefulnessLifecycle mWakefulnessLifecycle; - private final BatteryController mBatteryController; private final DozeHost mDozeHost; - private Part[] mParts; + private final UiModeManager mUiModeManager; + private final DockManager mDockManager; + private final Part[] mParts; private final ArrayList<State> mQueuedRequests = new ArrayList<>(); private State mState = State.UNINITIALIZED; private int mPulseReason; private boolean mWakeLockHeldForCurrentState = false; - private DockManager mDockManager; @Inject public DozeMachine(@WrappedService Service service, AmbientDisplayConfiguration config, WakeLock wakeLock, WakefulnessLifecycle wakefulnessLifecycle, - BatteryController batteryController, DozeLog dozeLog, DockManager dockManager, + UiModeManager uiModeManager, + DozeLog dozeLog, DockManager dockManager, DozeHost dozeHost, Part[] parts) { mDozeService = service; mConfig = config; mWakefulnessLifecycle = wakefulnessLifecycle; mWakeLock = wakeLock; - mBatteryController = batteryController; + mUiModeManager = uiModeManager; mDozeLog = dozeLog; mDockManager = dockManager; mDozeHost = dozeHost; @@ -244,7 +249,7 @@ public class DozeMachine { Assert.isMainThread(); if (isExecutingTransition()) { throw new IllegalStateException("Cannot get state because there were pending " - + "transitions: " + mQueuedRequests.toString()); + + "transitions: " + mQueuedRequests); } return mState; } @@ -313,11 +318,8 @@ public class DozeMachine { } mDozeLog.traceDozeStateSendComplete(newState); - switch (newState) { - case FINISH: - mDozeService.finish(); - break; - default: + if (newState == State.FINISH) { + mDozeService.finish(); } } @@ -357,6 +359,12 @@ public class DozeMachine { if (mState == State.FINISH) { return State.FINISH; } + if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR + && (requestedState.canPulse() || requestedState.staysAwake())) { + Log.i(TAG, "Doze is suppressed with all triggers disabled as car mode is active"); + mDozeLog.traceCarModeStarted(); + return State.DOZE_SUSPEND_TRIGGERS; + } if (mDozeHost.isAlwaysOnSuppressed() && requestedState.isAlwaysOn()) { Log.i(TAG, "Doze is suppressed by an app. Suppressing state: " + requestedState); mDozeLog.traceAlwaysOnSuppressed(requestedState, "app"); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java index 83220cab7149..60227ee95fc2 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java @@ -147,6 +147,7 @@ public class DozeScreenBrightness extends BroadcastReceiver implements DozeMachi setLightSensorEnabled(true); break; case DOZE: + case DOZE_SUSPEND_TRIGGERS: setLightSensorEnabled(false); resetBrightnessToDefault(); break; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java index 89f50ad9fc21..7ed4b35e1ee7 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSuppressor.java @@ -17,6 +17,7 @@ package com.android.systemui.doze; import static android.app.UiModeManager.ACTION_ENTER_CAR_MODE; +import static android.app.UiModeManager.ACTION_EXIT_CAR_MODE; import android.app.UiModeManager; import android.content.BroadcastReceiver; @@ -96,6 +97,7 @@ public class DozeSuppressor implements DozeMachine.Part { registerBroadcastReceiver(); mDozeHost.addCallback(mHostCallback); checkShouldImmediatelyEndDoze(); + checkShouldImmediatelySuspendDoze(); break; case FINISH: destroy(); @@ -110,11 +112,16 @@ public class DozeSuppressor implements DozeMachine.Part { mDozeHost.removeCallback(mHostCallback); } + private void checkShouldImmediatelySuspendDoze() { + if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) { + mDozeLog.traceCarModeStarted(); + mMachine.requestState(DozeMachine.State.DOZE_SUSPEND_TRIGGERS); + } + } + private void checkShouldImmediatelyEndDoze() { String reason = null; - if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) { - reason = "car_mode"; - } else if (!mDozeHost.isProvisioned()) { + if (!mDozeHost.isProvisioned()) { reason = "device_unprovisioned"; } else if (mBiometricUnlockControllerLazy.get().hasPendingAuthentication()) { reason = "has_pending_auth"; @@ -141,6 +148,7 @@ public class DozeSuppressor implements DozeMachine.Part { return; } IntentFilter filter = new IntentFilter(ACTION_ENTER_CAR_MODE); + filter.addAction(ACTION_EXIT_CAR_MODE); mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter); mBroadcastReceiverRegistered = true; } @@ -156,9 +164,14 @@ public class DozeSuppressor implements DozeMachine.Part { private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (ACTION_ENTER_CAR_MODE.equals(intent.getAction())) { - mDozeLog.traceImmediatelyEndDoze("car_mode"); - mMachine.requestState(DozeMachine.State.FINISH); + String action = intent.getAction(); + if (ACTION_ENTER_CAR_MODE.equals(action)) { + mDozeLog.traceCarModeStarted(); + mMachine.requestState(DozeMachine.State.DOZE_SUSPEND_TRIGGERS); + } else if (ACTION_EXIT_CAR_MODE.equals(action)) { + mDozeLog.traceCarModeEnded(); + mMachine.requestState(mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT) + ? DozeMachine.State.DOZE_AOD : DozeMachine.State.DOZE); } } }; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 1cc5df5d04cf..0014d6bbaf24 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -16,6 +16,10 @@ package com.android.systemui.doze; +import static com.android.systemui.doze.DozeMachine.State.DOZE_SUSPEND_TRIGGERS; +import static com.android.systemui.doze.DozeMachine.State.FINISH; +import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; + import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; @@ -437,14 +441,18 @@ public class DozeTriggers implements DozeMachine.Part { @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { + if (oldState == DOZE_SUSPEND_TRIGGERS && (newState != FINISH + && newState != UNINITIALIZED)) { + // Register callbacks that were unregistered when we switched to + // DOZE_SUSPEND_TRIGGERS state. + registerCallbacks(); + } switch (newState) { case INITIALIZED: mAodInterruptRunnable = null; sWakeDisplaySensorState = true; - mBroadcastReceiver.register(mBroadcastDispatcher); - mDockManager.addListener(mDockEventListener); + registerCallbacks(); mDozeSensors.requestTemporaryDisable(); - mDozeHost.addCallback(mHostCallback); break; case DOZE: case DOZE_AOD: @@ -472,21 +480,36 @@ public class DozeTriggers implements DozeMachine.Part { case DOZE_PULSE_DONE: mDozeSensors.requestTemporaryDisable(); break; + case DOZE_SUSPEND_TRIGGERS: case FINISH: - mBroadcastReceiver.unregister(mBroadcastDispatcher); - mDozeHost.removeCallback(mHostCallback); - mDockManager.removeListener(mDockEventListener); - mDozeSensors.setListening(false, false); - mDozeSensors.setProxListening(false); - mWantSensors = false; - mWantProxSensor = false; - mWantTouchScreenSensors = false; + stopListeningToAllTriggers(); break; default: } mDozeSensors.setListening(mWantSensors, mWantTouchScreenSensors); } + private void registerCallbacks() { + mBroadcastReceiver.register(mBroadcastDispatcher); + mDockManager.addListener(mDockEventListener); + mDozeHost.addCallback(mHostCallback); + } + + private void unregisterCallbacks() { + mBroadcastReceiver.unregister(mBroadcastDispatcher); + mDozeHost.removeCallback(mHostCallback); + mDockManager.removeListener(mDockEventListener); + } + + private void stopListeningToAllTriggers() { + unregisterCallbacks(); + mDozeSensors.setListening(false, false); + mDozeSensors.setProxListening(false); + mWantSensors = false; + mWantProxSensor = false; + mWantTouchScreenSensors = false; + } + @Override public void onScreenState(int state) { mDozeSensors.onScreenState(state); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index e568b8282856..70e4fa3aa27f 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -139,6 +139,7 @@ public class DozeUi implements DozeMachine.Part { break; case DOZE: case DOZE_AOD_PAUSED: + case DOZE_SUSPEND_TRIGGERS: unscheduleTimeTick(); break; case DOZE_REQUEST_PULSE: diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index 74949d094e33..d7b7777559da 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -183,22 +183,22 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve } private void updateBurnInOffsets() { - int burnInOffset = mMaxBurnInOffset; - // Make sure the offset starts at zero, to avoid a big jump in the overlay when it first // appears. - long millisSinceStart = System.currentTimeMillis() - mJitterStartTimeMillis; + final long millisSinceStart = System.currentTimeMillis() - mJitterStartTimeMillis; + final int burnInOffset; if (millisSinceStart < mMillisUntilFullJitter) { float lerpAmount = (float) millisSinceStart / (float) mMillisUntilFullJitter; - burnInOffset = Math.round(MathUtils.lerp(0f, burnInOffset, lerpAmount)); + burnInOffset = Math.round(MathUtils.lerp(0f, mMaxBurnInOffset, lerpAmount)); + } else { + burnInOffset = mMaxBurnInOffset; } // These translation values change slowly, and the set translation methods are idempotent, // so no translation occurs when the values don't change. - int burnInOffsetX = getBurnInOffset(burnInOffset * 2, true) - - burnInOffset; - int burnInOffsetY = getBurnInOffset(burnInOffset * 2, false) - - burnInOffset; + final int halfBurnInOffset = burnInOffset / 2; + final int burnInOffsetX = getBurnInOffset(burnInOffset, true) - halfBurnInOffset; + final int burnInOffsetY = getBurnInOffset(burnInOffset, false) - halfBurnInOffset; mView.setTranslationX(burnInOffsetX); mView.setTranslationY(burnInOffsetY); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java index 6589f26dbde2..3dd43866e465 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayNotificationCountProvider.java @@ -37,7 +37,7 @@ import javax.inject.Inject; /*** * {@link DreamOverlayNotificationCountProvider} provides the current notification count to - * registered callbacks. + * registered callbacks. Ongoing notifications are not included in the count. */ @SysUISingleton public class DreamOverlayNotificationCountProvider @@ -49,6 +49,10 @@ public class DreamOverlayNotificationCountProvider @Override public void onNotificationPosted( StatusBarNotification sbn, NotificationListenerService.RankingMap rankingMap) { + if (sbn.isOngoing()) { + // Don't count ongoing notifications. + return; + } mNotificationKeys.add(sbn.getKey()); reportNotificationCountChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java index a83e006dfa2f..7666eb84acac 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/SmartSpaceComplication.java @@ -26,7 +26,7 @@ import com.android.systemui.CoreStartable; import com.android.systemui.dreams.complication.Complication; import com.android.systemui.dreams.complication.ComplicationLayoutParams; import com.android.systemui.dreams.complication.ComplicationViewModel; -import com.android.systemui.dreams.smartspace.DreamsSmartspaceController; +import com.android.systemui.dreams.smartspace.DreamSmartspaceController; import com.android.systemui.plugins.BcSmartspaceDataPlugin; import java.util.List; @@ -43,7 +43,7 @@ public class SmartSpaceComplication implements Complication { * SystemUI. */ public static class Registrant extends CoreStartable { - private final DreamsSmartspaceController mSmartSpaceController; + private final DreamSmartspaceController mSmartSpaceController; private final DreamOverlayStateController mDreamOverlayStateController; private final SmartSpaceComplication mComplication; @@ -66,7 +66,7 @@ public class SmartSpaceComplication implements Complication { public Registrant(Context context, DreamOverlayStateController dreamOverlayStateController, SmartSpaceComplication smartSpaceComplication, - DreamsSmartspaceController smartSpaceController) { + DreamSmartspaceController smartSpaceController) { super(context); mDreamOverlayStateController = dreamOverlayStateController; mComplication = smartSpaceComplication; @@ -90,12 +90,12 @@ public class SmartSpaceComplication implements Complication { private static class SmartSpaceComplicationViewHolder implements ViewHolder { private static final int SMARTSPACE_COMPLICATION_WEIGHT = 10; - private final DreamsSmartspaceController mSmartSpaceController; + private final DreamSmartspaceController mSmartSpaceController; private final Context mContext; protected SmartSpaceComplicationViewHolder( Context context, - DreamsSmartspaceController smartSpaceController) { + DreamSmartspaceController smartSpaceController) { mSmartSpaceController = smartSpaceController; mContext = context; } @@ -120,12 +120,12 @@ public class SmartSpaceComplication implements Complication { } } - private final DreamsSmartspaceController mSmartSpaceController; + private final DreamSmartspaceController mSmartSpaceController; private final Context mContext; @Inject public SmartSpaceComplication(Context context, - DreamsSmartspaceController smartSpaceController) { + DreamSmartspaceController smartSpaceController) { mContext = context; mSmartSpaceController = smartSpaceController; } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java index f5c5a434a077..3d1bc59b433a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java @@ -18,10 +18,12 @@ package com.android.systemui.dreams.complication; import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS; import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_VIEW; +import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT; import android.app.smartspace.SmartspaceAction; import android.app.smartspace.SmartspaceTarget; import android.content.Context; +import android.content.Intent; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.text.TextUtils; @@ -31,6 +33,7 @@ import com.android.systemui.CoreStartable; import com.android.systemui.R; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent; +import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener; import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; import com.android.systemui.util.ViewController; @@ -132,15 +135,21 @@ public class DreamWeatherComplication implements Complication { */ static class DreamWeatherViewController extends ViewController<TextView> { private final LockscreenSmartspaceController mSmartSpaceController; + private final ActivityStarter mActivityStarter; + private final String mSmartspaceTrampolineActivityComponent; private SmartspaceTargetListener mSmartspaceTargetListener; @Inject DreamWeatherViewController( @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view, + @Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT) String smartspaceTrampoline, + ActivityStarter activityStarter, LockscreenSmartspaceController smartspaceController ) { super(view); + mActivityStarter = activityStarter; mSmartSpaceController = smartspaceController; + mSmartspaceTrampolineActivityComponent = smartspaceTrampoline; } @Override @@ -172,6 +181,15 @@ public class DreamWeatherComplication implements Complication { R.dimen.smart_action_button_icon_padding)); } + mView.setOnClickListener(v -> { + final Intent intent = headerAction.getIntent(); + if (intent != null && intent.getComponent() != null + && intent.getComponent().getClassName() + .equals(mSmartspaceTrampolineActivityComponent)) { + mActivityStarter.postStartActivityDismissingKeyguard( + intent, 0 /*delay*/); + } + }); } }); mSmartSpaceController.addListener(mSmartspaceTargetListener); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java index 536f3dcd2850..a1660f2b43f2 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java @@ -19,6 +19,7 @@ package com.android.systemui.dreams.complication.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; +import android.content.Context; import android.view.LayoutInflater; import android.view.ViewGroup; import android.widget.TextView; @@ -76,6 +77,7 @@ public interface DreamWeatherComplicationComponent { String DREAM_WEATHER_COMPLICATION_VIEW = "weather_complication_view"; String DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS = "weather_complication_layout_params"; + String SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT = "smartspace_trampoline_activity"; // Order weight of insert into parent container int INSERT_ORDER_WEIGHT = 1; @@ -106,5 +108,15 @@ public interface DreamWeatherComplicationComponent { ComplicationLayoutParams.DIRECTION_END, INSERT_ORDER_WEIGHT); } + + /** + * Provides the smartspace trampoline activity component. + */ + @Provides + @DreamWeatherComplicationScope + @Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT) + static String provideSmartspaceTrampolineActivityComponent(Context context) { + return context.getString(R.string.config_smartspaceTrampolineActivityComponent); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java index c1dff248818f..cd23f149cf7d 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java @@ -17,10 +17,14 @@ package com.android.systemui.dreams.dagger; import android.content.Context; +import android.content.res.Resources; import com.android.settingslib.dream.DreamBackend; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule; +import javax.inject.Named; + import dagger.Module; import dagger.Provides; @@ -34,6 +38,10 @@ import dagger.Provides; DreamOverlayComponent.class, }) public interface DreamModule { + String DREAM_ONLY_ENABLED_FOR_SYSTEM_USER = "dream_only_enabled_for_system_user"; + + String DREAM_SUPPORTED = "dream_supported"; + /** * Provides an instance of the dream backend. */ @@ -41,4 +49,19 @@ public interface DreamModule { static DreamBackend providesDreamBackend(Context context) { return DreamBackend.getInstance(context); } + + /** */ + @Provides + @Named(DREAM_ONLY_ENABLED_FOR_SYSTEM_USER) + static boolean providesDreamOnlyEnabledForSystemUser(@Main Resources resources) { + return resources.getBoolean( + com.android.internal.R.bool.config_dreamsOnlyEnabledForSystemUser); + } + + /** */ + @Provides + @Named(DREAM_SUPPORTED) + static boolean providesDreamSupported(@Main Resources resources) { + return resources.getBoolean(com.android.internal.R.bool.config_dreamsSupported); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt index a3095472783b..da2cf84318de 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamsSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt @@ -46,7 +46,7 @@ import javax.inject.Named * Controller for managing the smartspace view on the dream */ @SysUISingleton -class DreamsSmartspaceController @Inject constructor( +class DreamSmartspaceController @Inject constructor( private val context: Context, private val smartspaceManager: SmartspaceManager, private val execution: Execution, @@ -58,7 +58,7 @@ class DreamsSmartspaceController @Inject constructor( @Named(DREAM_SMARTSPACE_DATA_PLUGIN) optionalPlugin: Optional<BcSmartspaceDataPlugin> ) { companion object { - private const val TAG = "DreamsSmartspaceCtrlr" + private const val TAG = "DreamSmartspaceCtrlr" } private var session: SmartspaceSession? = null diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java index fbca7b1d86d3..f769a2355409 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandler.java @@ -44,6 +44,7 @@ import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent import com.android.wm.shell.animation.FlingAnimationUtils; import java.util.Optional; + import javax.inject.Inject; import javax.inject.Named; diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java index 4965c9dfd00b..3087cdfd0cc0 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/HideComplicationTouchHandler.java @@ -25,6 +25,7 @@ import android.view.View; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.complication.Complication; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.touch.TouchInsetManager; import com.google.common.util.concurrent.ListenableFuture; @@ -50,6 +51,7 @@ public class HideComplicationTouchHandler implements DreamTouchHandler { private final Complication.VisibilityController mVisibilityController; private final int mRestoreTimeout; + private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final Handler mHandler; private final Executor mExecutor; private final TouchInsetManager mTouchInsetManager; @@ -65,10 +67,12 @@ public class HideComplicationTouchHandler implements DreamTouchHandler { HideComplicationTouchHandler(Complication.VisibilityController visibilityController, @Named(COMPLICATIONS_RESTORE_TIMEOUT) int restoreTimeout, TouchInsetManager touchInsetManager, + StatusBarKeyguardViewManager statusBarKeyguardViewManager, @Main Executor executor, @Main Handler handler) { mVisibilityController = visibilityController; mRestoreTimeout = restoreTimeout; + mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mHandler = handler; mTouchInsetManager = touchInsetManager; mExecutor = executor; @@ -80,10 +84,13 @@ public class HideComplicationTouchHandler implements DreamTouchHandler { Log.d(TAG, "onSessionStart"); } + final boolean bouncerShowing = mStatusBarKeyguardViewManager.isBouncerShowing(); + // If other sessions are interested in this touch, do not fade out elements. - if (session.getActiveSessionCount() > 1) { + if (session.getActiveSessionCount() > 1 || bouncerShowing) { if (DEBUG) { - Log.d(TAG, "multiple active touch sessions, not fading"); + Log.d(TAG, "not fading. Active session count: " + session.getActiveSessionCount() + + ". Bouncer showing: " + bouncerShowing); } session.pop(); return; diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index afa7d5e0a9c4..55bbcb654cf5 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -158,12 +158,21 @@ public class Flags { // 1000 - dock public static final BooleanFlag SIMULATE_DOCK_THROUGH_CHARGING = new BooleanFlag(1000, true); + public static final BooleanFlag DOCK_SETUP_ENABLED = new BooleanFlag(1001, false); + // 1100 - windowing @Keep public static final SysPropBooleanFlag WM_ENABLE_SHELL_TRANSITIONS = new SysPropBooleanFlag(1100, "persist.wm.debug.shell_transit", false); + /** + * b/170163464: animate bubbles expanded view collapse with home gesture + */ + @Keep + public static final SysPropBooleanFlag BUBBLES_HOME_GESTURE = + new SysPropBooleanFlag(1101, "persist.wm.debug.bubbles_home_gesture", false); + // 1200 - predictive back @Keep public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index e379d766f0ca..7e3d25373906 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -40,6 +40,7 @@ import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.PendingIntent; import android.app.StatusBarManager; +import android.app.WindowConfiguration; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -921,6 +922,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } final RemoteAnimationTarget primary = apps[0]; + final boolean isDream = (apps[0].taskInfo.topActivityType + == WindowConfiguration.ACTIVITY_TYPE_DREAM); final SyncRtSurfaceTransactionApplier applier = new SyncRtSurfaceTransactionApplier( @@ -942,20 +945,24 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, final float surfaceHeight = primary.screenSpaceBounds.height(); - mUnoccludeMatrix.setTranslate( - 0f, - (1f - animatedValue) - * surfaceHeight - * UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT); - - SyncRtSurfaceTransactionApplier.SurfaceParams params = + // Fade for all types of activities. + SyncRtSurfaceTransactionApplier.SurfaceParams.Builder + paramsBuilder = new SyncRtSurfaceTransactionApplier.SurfaceParams .Builder(primary.leash) - .withMatrix(mUnoccludeMatrix) - .withCornerRadius(mWindowCornerRadius) - .withAlpha(animatedValue) - .build(); - applier.scheduleApply(params); + .withAlpha(animatedValue); + // Set translate if the occluding activity isn't Dream. + if (!isDream) { + mUnoccludeMatrix.setTranslate( + 0f, + (1f - animatedValue) + * surfaceHeight + * UNOCCLUDE_TRANSLATE_DISTANCE_PERCENT); + + paramsBuilder.withMatrix(mUnoccludeMatrix).withCornerRadius( + mWindowCornerRadius); + } + applier.scheduleApply(paramsBuilder.build()); }); mUnoccludeAnimator.addListener(new AnimatorListenerAdapter() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index dea13251e100..e43ae0fccca7 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -70,6 +70,7 @@ import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.animation.GhostedViewLaunchAnimatorController; import com.android.systemui.animation.Interpolators; +import com.android.systemui.bluetooth.BroadcastDialogController; import com.android.systemui.broadcast.BroadcastSender; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; @@ -188,6 +189,11 @@ public class MediaControlPanel { private final SeekBarViewModel.EnabledChangeListener mEnabledChangeListener = this::setIsSeekBarEnabled; + private final BroadcastDialogController mBroadcastDialogController; + private boolean mIsCurrentBroadcastedApp = false; + private boolean mShowBroadcastDialogButton = false; + private String mSwitchBroadcastApp; + /** * Initialize a new control panel * @@ -213,7 +219,8 @@ public class MediaControlPanel { MediaUiEventLogger logger, KeyguardStateController keyguardStateController, ActivityIntentHelper activityIntentHelper, - NotificationLockscreenUserManager lockscreenUserManager) { + NotificationLockscreenUserManager lockscreenUserManager, + BroadcastDialogController broadcastDialogController) { mContext = context; mBackgroundExecutor = backgroundExecutor; mMainExecutor = mainExecutor; @@ -230,6 +237,7 @@ public class MediaControlPanel { mKeyguardStateController = keyguardStateController; mActivityIntentHelper = activityIntentHelper; mLockscreenUserManager = lockscreenUserManager; + mBroadcastDialogController = broadcastDialogController; mSeekBarViewModel.setLogSeek(() -> { if (mPackageName != null && mInstanceId != null) { @@ -449,7 +457,10 @@ public class MediaControlPanel { final MediaController controller = getController(); mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller)); - bindOutputSwitcherChip(data); + // Show the broadcast dialog button only when the le audio is enabled. + mShowBroadcastDialogButton = + data.getDevice() != null && data.getDevice().getShowBroadcastButton(); + bindOutputSwitcherAndBroadcastButton(mShowBroadcastDialogButton, data); bindGutsMenuForPlayer(data); bindPlayerContentDescription(data); bindScrubbingTime(data); @@ -467,21 +478,40 @@ public class MediaControlPanel { Trace.endSection(); } - private void bindOutputSwitcherChip(MediaData data) { - // Output switcher chip + private void bindOutputSwitcherAndBroadcastButton(boolean showBroadcastButton, MediaData data) { ViewGroup seamlessView = mMediaViewHolder.getSeamless(); seamlessView.setVisibility(View.VISIBLE); ImageView iconView = mMediaViewHolder.getSeamlessIcon(); TextView deviceName = mMediaViewHolder.getSeamlessText(); final MediaDeviceData device = data.getDevice(); - // Disable clicking on output switcher for invalid devices and resumption controls - final boolean seamlessDisabled = (device != null && !device.getEnabled()) - || data.getResumption(); - final float seamlessAlpha = seamlessDisabled ? DISABLED_ALPHA : 1.0f; - mMediaViewHolder.getSeamlessButton().setAlpha(seamlessAlpha); - seamlessView.setEnabled(!seamlessDisabled); - CharSequence deviceString = mContext.getString(R.string.media_seamless_other_device); + final boolean enabled; + final boolean seamlessDisabled; + final int iconResource; + CharSequence deviceString; + if (showBroadcastButton) { + // TODO(b/233698402): Use the package name instead of app label to avoid the + // unexpected result. + mIsCurrentBroadcastedApp = device != null + && TextUtils.equals(device.getName(), + MediaDataUtils.getAppLabel(mContext, mPackageName, mContext.getString( + R.string.bt_le_audio_broadcast_dialog_unknown_name))); + seamlessDisabled = !mIsCurrentBroadcastedApp; + // Always be enabled if the broadcast button is shown + enabled = true; + deviceString = mContext.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name); + iconResource = R.drawable.settings_input_antenna; + } else { + // Disable clicking on output switcher for invalid devices and resumption controls + seamlessDisabled = (device != null && !device.getEnabled()) || data.getResumption(); + enabled = !seamlessDisabled; + deviceString = mContext.getString(R.string.media_seamless_other_device); + iconResource = R.drawable.ic_media_home_devices; + } + + mMediaViewHolder.getSeamlessButton().setAlpha(seamlessDisabled ? DISABLED_ALPHA : 1.0f); + seamlessView.setEnabled(enabled); + if (device != null) { Drawable icon = device.getIcon(); if (icon instanceof AdaptiveIcon) { @@ -494,7 +524,7 @@ public class MediaControlPanel { deviceString = device.getName(); } else { // Set to default icon - iconView.setImageResource(R.drawable.ic_media_home_devices); + iconView.setImageResource(iconResource); } deviceName.setText(deviceString); seamlessView.setContentDescription(deviceString); @@ -503,21 +533,39 @@ public class MediaControlPanel { if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { return; } - mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId); - if (device.getIntent() != null) { - if (device.getIntent().isActivity()) { - mActivityStarter.startActivity( - device.getIntent().getIntent(), true); + + if (showBroadcastButton) { + // If the current media app is not broadcasted and users press the outputer + // button, we should pop up the broadcast dialog to check do they want to + // switch broadcast to the other media app, otherwise we still pop up the + // media output dialog. + if (!mIsCurrentBroadcastedApp) { + mLogger.logOpenBroadcastDialog(mUid, mPackageName, mInstanceId); + mSwitchBroadcastApp = device.getName().toString(); + mBroadcastDialogController.createBroadcastDialog(mSwitchBroadcastApp, + mPackageName, true, mMediaViewHolder.getSeamlessButton()); } else { - try { - device.getIntent().send(); - } catch (PendingIntent.CanceledException e) { - Log.e(TAG, "Device pending intent was canceled"); - } + mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId); + mMediaOutputDialogFactory.create(mPackageName, true, + mMediaViewHolder.getSeamlessButton()); } } else { - mMediaOutputDialogFactory.create(mPackageName, true, - mMediaViewHolder.getSeamlessButton()); + mLogger.logOpenOutputSwitcher(mUid, mPackageName, mInstanceId); + if (device.getIntent() != null) { + if (device.getIntent().isActivity()) { + mActivityStarter.startActivity( + device.getIntent().getIntent(), true); + } else { + try { + device.getIntent().send(); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, "Device pending intent was canceled"); + } + } + } else { + mMediaOutputDialogFactory.create(mPackageName, true, + mMediaViewHolder.getSeamlessButton()); + } } }); } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt index 360f86548e13..b2ab12a81bcc 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt @@ -215,5 +215,8 @@ data class MediaDeviceData val intent: PendingIntent? = null, /** Unique id for this device */ - val id: String? = null + val id: String? = null, + + /** Whether or not to show the broadcast button */ + val showBroadcastButton: Boolean ) diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 6a69d427929e..30ba476abce2 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -686,7 +686,8 @@ class MediaDataManager( val enabled = deviceIntent != null && deviceIntent.isActivity val deviceDrawable = Icon.createWithResource(sbn.packageName, deviceIcon) .loadDrawable(sbn.getPackageContext(context)) - device = MediaDeviceData(enabled, deviceDrawable, deviceName, deviceIntent) + device = MediaDeviceData(enabled, deviceDrawable, deviceName, deviceIntent, + showBroadcastButton = false) } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataUtils.java b/packages/SystemUI/src/com/android/systemui/media/MediaDataUtils.java new file mode 100644 index 000000000000..b8185b9de7e8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataUtils.java @@ -0,0 +1,43 @@ +/** + * Copyright (C) 2022 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.media; + +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.text.TextUtils; + +public class MediaDataUtils { + + public static String getAppLabel(Context context, String packageName, String unknownName) { + if (TextUtils.isEmpty(packageName)) { + return null; + } + final PackageManager packageManager = context.getPackageManager(); + ApplicationInfo applicationInfo; + try { + applicationInfo = packageManager.getApplicationInfo(packageName, 0); + } catch (PackageManager.NameNotFoundException e) { + applicationInfo = null; + } + final String applicationName = + (String) (applicationInfo != null + ? packageManager.getApplicationLabel(applicationInfo) + : unknownName); + return applicationName; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt index 8558859638d5..b552d9fb584a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDeviceManager.kt @@ -16,15 +16,23 @@ package com.android.systemui.media +import android.bluetooth.BluetoothLeBroadcast +import android.bluetooth.BluetoothLeBroadcastMetadata +import android.content.Context import android.graphics.drawable.Drawable import android.media.MediaRouter2Manager import android.media.session.MediaController +import android.text.TextUtils +import android.util.Log import androidx.annotation.AnyThread import androidx.annotation.MainThread import androidx.annotation.WorkerThread +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast +import com.android.settingslib.bluetooth.LocalBluetoothManager import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice import com.android.systemui.Dumpable +import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dump.DumpManager @@ -36,16 +44,20 @@ import java.util.concurrent.Executor import javax.inject.Inject private const val PLAYBACK_TYPE_UNKNOWN = 0 +private const val TAG = "MediaDeviceManager" +private const val DEBUG = true /** * Provides information about the route (ie. device) where playback is occurring. */ class MediaDeviceManager @Inject constructor( + private val context: Context, private val controllerFactory: MediaControllerFactory, private val localMediaManagerFactory: LocalMediaManagerFactory, private val mr2manager: MediaRouter2Manager, private val muteAwaitConnectionManagerFactory: MediaMuteAwaitConnectionManagerFactory, private val configurationController: ConfigurationController, + private val localBluetoothManager: LocalBluetoothManager?, @Main private val fgExecutor: Executor, @Background private val bgExecutor: Executor, dumpManager: DumpManager @@ -147,7 +159,8 @@ class MediaDeviceManager @Inject constructor( val controller: MediaController?, val localMediaManager: LocalMediaManager, val muteAwaitConnectionManager: MediaMuteAwaitConnectionManager? - ) : LocalMediaManager.DeviceCallback, MediaController.Callback() { + ) : LocalMediaManager.DeviceCallback, MediaController.Callback(), + BluetoothLeBroadcast.Callback { val token get() = controller?.sessionToken @@ -166,7 +179,7 @@ class MediaDeviceManager @Inject constructor( // A device that is not yet connected but is expected to connect imminently. Because it's // expected to connect imminently, it should be displayed as the current device. private var aboutToConnectDeviceOverride: AboutToConnectDevice? = null - + private var broadcastDescription: String? = null private val configListener = object : ConfigurationController.ConfigurationListener { override fun onLocaleListChanged() { updateCurrent() @@ -238,7 +251,11 @@ class MediaDeviceManager @Inject constructor( ) { aboutToConnectDeviceOverride = AboutToConnectDevice( fullMediaDevice = localMediaManager.getMediaDeviceById(deviceAddress), - backupMediaDeviceData = MediaDeviceData(enabled = true, deviceIcon, deviceName) + backupMediaDeviceData = MediaDeviceData( + /* enabled */ enabled = true, + /* icon */ deviceIcon, + /* name */ deviceName, + /* showBroadcastButton */ showBroadcastButton = false) ) updateCurrent() } @@ -248,23 +265,127 @@ class MediaDeviceManager @Inject constructor( updateCurrent() } + + override fun onBroadcastStarted(reason: Int, broadcastId: Int) { + if (DEBUG) { + Log.d(TAG, "onBroadcastStarted(), reason = $reason , broadcastId = $broadcastId") + } + updateCurrent() + } + + override fun onBroadcastStartFailed(reason: Int) { + if (DEBUG) { + Log.d(TAG, "onBroadcastStartFailed(), reason = $reason") + } + } + + override fun onBroadcastMetadataChanged(broadcastId: Int, + metadata: BluetoothLeBroadcastMetadata) { + if (DEBUG) { + Log.d(TAG, "onBroadcastMetadataChanged(), broadcastId = $broadcastId , " + + "metadata = $metadata") + } + updateCurrent() + } + + override fun onBroadcastStopped(reason: Int, broadcastId: Int) { + if (DEBUG) { + Log.d(TAG, "onBroadcastStopped(), reason = $reason , broadcastId = $broadcastId") + + } + updateCurrent() + } + + override fun onBroadcastStopFailed(reason: Int) { + if (DEBUG) { + Log.d(TAG, "onBroadcastStopFailed(), reason = $reason") + } + } + + override fun onBroadcastUpdated(reason: Int, broadcastId: Int) { + if (DEBUG) { + Log.d(TAG, "onBroadcastUpdated(), reason = $reason , broadcastId = $broadcastId") + } + updateCurrent() + } + + override fun onBroadcastUpdateFailed(reason: Int, broadcastId: Int) { + if (DEBUG) { + Log.d(TAG, "onBroadcastUpdateFailed(), reason = $reason , " + + "broadcastId = $broadcastId") + } + } + + override fun onPlaybackStarted(reason: Int, broadcastId: Int) {} + + override fun onPlaybackStopped(reason: Int, broadcastId: Int) {} + @WorkerThread private fun updateCurrent() { - val aboutToConnect = aboutToConnectDeviceOverride - if (aboutToConnect != null && - aboutToConnect.fullMediaDevice == null && - aboutToConnect.backupMediaDeviceData != null) { + if (isLeAudioBroadcastEnabled()) { + current = MediaDeviceData( + /* enabled */ true, + /* icon */ context.getDrawable(R.drawable.settings_input_antenna), + /* name */ broadcastDescription, + /* intent */ null, + /* showBroadcastButton */ showBroadcastButton = true) + } else { + val aboutToConnect = aboutToConnectDeviceOverride + if (aboutToConnect != null && + aboutToConnect.fullMediaDevice == null && + aboutToConnect.backupMediaDeviceData != null) { // Only use [backupMediaDeviceData] when we don't have [fullMediaDevice]. current = aboutToConnect.backupMediaDeviceData return + } + val device = aboutToConnect?.fullMediaDevice + ?: localMediaManager.currentConnectedDevice + val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) } + + // If we have a controller but get a null route, then don't trust the device + val enabled = device != null && (controller == null || route != null) + val name = route?.name?.toString() ?: device?.name + current = MediaDeviceData(enabled, device?.iconWithoutBackground, name, + id = device?.id, showBroadcastButton = false) } - val device = aboutToConnect?.fullMediaDevice ?: localMediaManager.currentConnectedDevice - val route = controller?.let { mr2manager.getRoutingSessionForMediaController(it) } + } - // If we have a controller but get a null route, then don't trust the device - val enabled = device != null && (controller == null || route != null) - val name = route?.name?.toString() ?: device?.name - current = MediaDeviceData(enabled, device?.iconWithoutBackground, name, id = device?.id) + private fun isLeAudioBroadcastEnabled(): Boolean { + if (localBluetoothManager != null) { + val profileManager = localBluetoothManager.profileManager + if (profileManager != null) { + val bluetoothLeBroadcast = profileManager.leAudioBroadcastProfile + if (bluetoothLeBroadcast != null && bluetoothLeBroadcast.isEnabled(null)) { + getBroadcastingInfo(bluetoothLeBroadcast) + return true + } else if (DEBUG) { + Log.d(TAG, "Can not get LocalBluetoothLeBroadcast") + } + } else if (DEBUG) { + Log.d(TAG, "Can not get LocalBluetoothProfileManager") + } + } else if (DEBUG) { + Log.d(TAG, "Can not get LocalBluetoothManager") + } + return false + } + + private fun getBroadcastingInfo(bluetoothLeBroadcast: LocalBluetoothLeBroadcast) { + var currentBroadcastedApp = bluetoothLeBroadcast.appSourceName + // TODO(b/233698402): Use the package name instead of app label to avoid the + // unexpected result. + // Check the current media app's name is the same with current broadcast app's name + // or not. + var mediaApp = MediaDataUtils.getAppLabel( + context, localMediaManager.packageName, + context.getString(R.string.bt_le_audio_broadcast_dialog_unknown_name)) + var isCurrentBroadcastedApp = TextUtils.equals(mediaApp, currentBroadcastedApp) + if (isCurrentBroadcastedApp) { + broadcastDescription = context.getString( + R.string.broadcasting_description_is_broadcasting) + } else { + broadcastDescription = currentBroadcastedApp + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt index 52f5cc568ba4..0baf01e7476f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaUiEventLogger.kt @@ -176,6 +176,11 @@ class MediaUiEventLogger @Inject constructor(private val logger: UiEventLogger) logger.logWithInstanceId(MediaUiEvent.MEDIA_RECOMMENDATION_CARD_TAP, 0, packageName, instanceId) } + + fun logOpenBroadcastDialog(uid: Int, packageName: String, instanceId: InstanceId) { + logger.logWithInstanceId(MediaUiEvent.MEDIA_OPEN_BROADCAST_DIALOG, uid, packageName, + instanceId) + } } enum class MediaUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum { @@ -273,7 +278,10 @@ enum class MediaUiEvent(val metricId: Int) : UiEventLogger.UiEventEnum { MEDIA_RECOMMENDATION_ITEM_TAP(1044), @UiEvent(doc = "User tapped on a media recommendation card") - MEDIA_RECOMMENDATION_CARD_TAP(1045); + MEDIA_RECOMMENDATION_CARD_TAP(1045), + + @UiEvent(doc = "User opened the broadcast dialog from a media control") + MEDIA_OPEN_BROADCAST_DIALOG(1079); override fun getId() = metricId }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index aa38b781229c..47b1bff29a10 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -215,8 +215,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements private final UiEventLogger mUiEventLogger; private final NavBarHelper mNavBarHelper; private final NotificationShadeDepthController mNotificationShadeDepthController; - private final UserContextProvider mUserContextProvider; private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener; + private final UserContextProvider mUserContextProvider; private final RegionSamplingHelper mRegionSamplingHelper; private final int mNavColorSampleMargin; private NavigationBarFrame mFrame; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index 3fc9afe6ea94..ad3cfa359a52 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -812,6 +812,7 @@ public class NavigationBarView extends FrameLayout { mImeDrawsImeNavBar = imeDrawsImeNavBar; mBarTransitions.onNavigationModeChanged(mNavBarMode); mEdgeBackGestureHandler.onNavigationModeChanged(mNavBarMode); + mRotationButtonController.onNavigationModeChanged(mNavBarMode); updateRotationButton(); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt new file mode 100644 index 000000000000..56ad19ae89ca --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt @@ -0,0 +1,340 @@ +package com.android.systemui.navigationbar.gestural + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Path +import android.graphics.RectF +import android.view.View +import androidx.dynamicanimation.animation.FloatPropertyCompat +import androidx.dynamicanimation.animation.SpringAnimation +import androidx.dynamicanimation.animation.SpringForce +import com.android.internal.util.LatencyTracker +import com.android.settingslib.Utils +import com.android.systemui.navigationbar.gestural.BackPanelController.DelayedOnAnimationEndListener + +private const val TAG = "BackPanel" +private const val DEBUG = false + +class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : View(context) { + + var arrowsPointLeft = false + set(value) { + if (field != value) { + invalidate() + field = value + } + } + + // Arrow color and shape + private val arrowPath = Path() + private val arrowPaint = Paint() + + // Arrow background color and shape + private var arrowBackgroundRect = RectF() + private var arrowBackgroundPaint = Paint() + + // True if the panel is currently on the left of the screen + var isLeftPanel = false + + /** + * Used to track back arrow latency from [android.view.MotionEvent.ACTION_DOWN] to [onDraw] + */ + private var trackingBackArrowLatency = false + + /** + * The length of the arrow measured horizontally. Used for animating [arrowPath] + */ + private var arrowLength = AnimatedFloat("arrowLength", SpringForce()) + + /** + * The height of the arrow measured vertically from its center to its top (i.e. half the total + * height). Used for animating [arrowPath] + */ + private var arrowHeight = AnimatedFloat("arrowHeight", SpringForce()) + + private val backgroundWidth = AnimatedFloat( + name = "backgroundWidth", + SpringForce().apply { + stiffness = 600f + dampingRatio = 0.65f + }) + + private val backgroundHeight = AnimatedFloat( + name = "backgroundHeight", + SpringForce().apply { + stiffness = 600f + dampingRatio = 0.65f + }) + + /** + * Corners of the background closer to the edge of the screen (where the arrow appeared from). + * Used for animating [arrowBackgroundRect] + */ + private val backgroundEdgeCornerRadius = AnimatedFloat( + name = "backgroundEdgeCornerRadius", + SpringForce().apply { + stiffness = 400f + dampingRatio = SpringForce.DAMPING_RATIO_MEDIUM_BOUNCY + }) + + /** + * Corners of the background further from the edge of the screens (toward the direction the + * arrow is being dragged). Used for animating [arrowBackgroundRect] + */ + private val backgroundDragCornerRadius = AnimatedFloat( + name = "backgroundDragCornerRadius", + SpringForce().apply { + stiffness = 2200f + dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY + }) + + /** + * Left/right position of the background relative to the canvas. Also corresponds with the + * background's margin relative to the screen edge. The arrow will be centered within the + * background. + */ + private var horizontalTranslation = AnimatedFloat("horizontalTranslation", SpringForce()) + + /** + * Canvas vertical translation. How far up/down the arrow and background appear relative to the + * canvas. + */ + private var verticalTranslation: AnimatedFloat = + AnimatedFloat("verticalTranslation", SpringForce().apply { + stiffness = SpringForce.STIFFNESS_MEDIUM + }) + + /** + * Use for drawing debug info. Can only be set if [DEBUG]=true + */ + var drawDebugInfo: ((canvas: Canvas) -> Unit)? = null + set(value) { + if (DEBUG) field = value + } + + internal fun updateArrowPaint(arrowThickness: Float) { + // Arrow constants + arrowPaint.strokeWidth = arrowThickness + + arrowPaint.color = + Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary) + arrowBackgroundPaint.color = Utils.getColorAccentDefaultColor(context) + } + + private inner class AnimatedFloat(name: String, springForce: SpringForce) { + // The resting position when not stretched by a touch drag + private var restingPosition = 0f + + // The current position as updated by the SpringAnimation + var pos = 0f + set(v) { + if (field != v) { + field = v + invalidate() + } + } + + val animation: SpringAnimation + + init { + val floatProp = object : FloatPropertyCompat<AnimatedFloat>(name) { + override fun setValue(animatedFloat: AnimatedFloat, value: Float) { + animatedFloat.pos = value + } + + override fun getValue(animatedFloat: AnimatedFloat): Float = animatedFloat.pos + } + animation = SpringAnimation(this, floatProp) + animation.spring = springForce + } + + fun snapTo(newPosition: Float) { + animation.cancel() + restingPosition = newPosition + animation.spring.finalPosition = newPosition + pos = newPosition + } + + fun stretchTo(stretchAmount: Float) { + animation.animateToFinalPosition(restingPosition + stretchAmount) + } + + fun updateRestingPosition(pos: Float, animated: Boolean) { + restingPosition = pos + if (animated) + animation.animateToFinalPosition(restingPosition) + else + snapTo(restingPosition) + } + } + + init { + visibility = GONE + arrowPaint.apply { + style = Paint.Style.STROKE + strokeCap = Paint.Cap.SQUARE + } + arrowBackgroundPaint.apply { + style = Paint.Style.FILL + strokeJoin = Paint.Join.ROUND + strokeCap = Paint.Cap.ROUND + } + } + + private fun calculateArrowPath(dx: Float, dy: Float): Path { + arrowPath.reset() + arrowPath.moveTo(dx, -dy) + arrowPath.lineTo(0f, 0f) + arrowPath.lineTo(dx, dy) + arrowPath.moveTo(dx, -dy) + return arrowPath + } + + fun addEndListener(endListener: DelayedOnAnimationEndListener): Boolean { + return if (horizontalTranslation.animation.isRunning) { + horizontalTranslation.animation.addEndListener(endListener) + true + } else { + endListener.runNow() + false + } + } + + fun setStretch( + arrowLengthStretch: Float, + arrowHeightStretch: Float, + backgroundWidthStretch: Float, + backgroundHeightStretch: Float, + backgroundEdgeCornerRadiusStretch: Float, + backgroundDragCornerRadiusStretch: Float, + horizontalTranslationStretch: Float + ) { + arrowLength.stretchTo(arrowLengthStretch) + arrowHeight.stretchTo(arrowHeightStretch) + backgroundWidth.stretchTo(backgroundWidthStretch) + backgroundHeight.stretchTo(backgroundHeightStretch) + backgroundEdgeCornerRadius.stretchTo(backgroundEdgeCornerRadiusStretch) + backgroundDragCornerRadius.stretchTo(backgroundDragCornerRadiusStretch) + horizontalTranslation.stretchTo(horizontalTranslationStretch) + } + + fun resetStretch() { + setStretch(0f, 0f, 0f, 0f, 0f, 0f, 0f) + } + + /** + * Updates resting arrow and background size not accounting for stretch + */ + internal fun updateRestingArrowDimens( + backgroundWidth: Float, + backgroundHeight: Float, + backgroundEdgeCornerRadius: Float, + backgroundDragCornerRadius: Float, + arrowLength: Float, + arrowHeight: Float, + horizontalTranslation: Float, + animate: Boolean + ) { + this.arrowLength.updateRestingPosition(arrowLength, animate) + this.arrowHeight.updateRestingPosition(arrowHeight, animate) + this.backgroundWidth.updateRestingPosition(backgroundWidth, animate) + this.backgroundHeight.updateRestingPosition(backgroundHeight, animate) + this.backgroundEdgeCornerRadius.updateRestingPosition(backgroundEdgeCornerRadius, animate) + this.backgroundDragCornerRadius.updateRestingPosition(backgroundDragCornerRadius, animate) + this.horizontalTranslation.updateRestingPosition(horizontalTranslation, animate) + } + + fun animateVertically(yPos: Float) = verticalTranslation.stretchTo(yPos) + + fun setArrowStiffness(arrowStiffness: Float, arrowDampingRatio: Float) { + arrowLength.animation.spring.apply { + stiffness = arrowStiffness + dampingRatio = arrowDampingRatio + } + arrowHeight.animation.spring.apply { + stiffness = arrowStiffness + dampingRatio = arrowDampingRatio + } + } + + override fun hasOverlappingRendering() = false + + override fun onDraw(canvas: Canvas) { + var edgeCorner = backgroundEdgeCornerRadius.pos + val farCorner = backgroundDragCornerRadius.pos + val halfHeight = backgroundHeight.pos / 2 + + canvas.save() + + if (!isLeftPanel) canvas.scale(-1f, 1f, width / 2.0f, 0f) + + canvas.translate( + horizontalTranslation.pos, + height * 0.5f + verticalTranslation.pos + ) + + val arrowBackground = arrowBackgroundRect.apply { + left = 0f + top = -halfHeight + right = backgroundWidth.pos + bottom = halfHeight + }.toPathWithRoundCorners( + topLeft = edgeCorner, + bottomLeft = edgeCorner, + topRight = farCorner, + bottomRight = farCorner + ) + canvas.drawPath(arrowBackground, arrowBackgroundPaint) + + val dx = arrowLength.pos + val dy = arrowHeight.pos + + // How far the arrow bounding box should be from the edge of the screen. Measured from + // either the tip or the back of the arrow, whichever is closer + var arrowOffset = (backgroundWidth.pos - dx) / 2 + canvas.translate( + /* dx= */ arrowOffset, + /* dy= */ 0f /* pass 0 for the y position since the canvas was already translated */ + ) + + val arrowPointsAwayFromEdge = !arrowsPointLeft.xor(isLeftPanel) + if (arrowPointsAwayFromEdge) { + canvas.apply { + scale(-1f, 1f, 0f, 0f) + translate(-dx, 0f) + } + } + + val arrowPath = calculateArrowPath(dx = dx, dy = dy) + canvas.drawPath(arrowPath, arrowPaint) + canvas.restore() + + if (trackingBackArrowLatency) { + latencyTracker.onActionEnd(LatencyTracker.ACTION_SHOW_BACK_ARROW) + trackingBackArrowLatency = false + } + + if (DEBUG) drawDebugInfo?.invoke(canvas) + } + + fun startTrackingShowBackArrowLatency() { + latencyTracker.onActionStart(LatencyTracker.ACTION_SHOW_BACK_ARROW) + trackingBackArrowLatency = true + } + + private fun RectF.toPathWithRoundCorners( + topLeft: Float = 0f, + topRight: Float = 0f, + bottomRight: Float = 0f, + bottomLeft: Float = 0f + ): Path = Path().apply { + val corners = floatArrayOf( + topLeft, topLeft, + topRight, topRight, + bottomRight, bottomRight, + bottomLeft, bottomLeft + ) + addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt new file mode 100644 index 000000000000..100411b1cb93 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt @@ -0,0 +1,735 @@ +/* + * Copyright (C) 2022 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.navigationbar.gestural + +import android.content.Context +import android.content.res.Configuration +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Point +import android.os.Handler +import android.os.SystemClock +import android.os.VibrationEffect +import android.util.Log +import android.util.MathUtils.constrain +import android.util.MathUtils.saturate +import android.view.Gravity +import android.view.MotionEvent +import android.view.VelocityTracker +import android.view.View +import android.view.WindowManager +import android.view.animation.AccelerateInterpolator +import android.view.animation.PathInterpolator +import android.window.BackEvent +import androidx.dynamicanimation.animation.DynamicAnimation +import androidx.dynamicanimation.animation.SpringForce +import com.android.internal.util.LatencyTracker +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.NavigationEdgeBackPlugin +import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.util.ViewController +import com.android.wm.shell.back.BackAnimation +import java.io.PrintWriter +import javax.inject.Inject +import kotlin.math.abs +import kotlin.math.max +import kotlin.math.min +import kotlin.math.sign + +private const val TAG = "BackPanelController" +private const val DEBUG = false + +private const val ENABLE_FAILSAFE = true + +private const val FAILSAFE_DELAY_MS: Long = 350 + +/** + * The time required between the arrow-appears vibration effect and the back-committed vibration + * effect. If the arrow is flung quickly, the phone only vibrates once. However, if the arrow is + * held on the screen for a long time, it will vibrate a second time when the back gesture is + * committed. + */ +private const val GESTURE_DURATION_FOR_CLICK_MS = 400 + +/** + * The min duration arrow remains on screen during a fling event. + */ +private const val FLING_PAUSE_DURATION_MS = 50L + +/** + * The min duration arrow remains on screen during a fling event. + */ +private const val MIN_FLING_VELOCITY = 3000 + +/** + * The amount of rubber banding we do for the vertical translation + */ +private const val RUBBER_BAND_AMOUNT = 15 + +private const val ARROW_APPEAR_STIFFNESS = 600f +private const val ARROW_APPEAR_DAMPING_RATIO = 0.4f +private const val ARROW_DISAPPEAR_STIFFNESS = 1200f +private const val ARROW_DISAPPEAR_DAMPING_RATIO = SpringForce.DAMPING_RATIO_NO_BOUNCY + +/** + * The interpolator used to rubber band + */ +private val RUBBER_BAND_INTERPOLATOR = PathInterpolator(1.0f / 5.0f, 1.0f, 1.0f, 1.0f) + +private val ACCELERATE_INTERPOLATOR = AccelerateInterpolator(0.7f) + +class BackPanelController private constructor( + context: Context, + private var backAnimation: BackAnimation?, + private val windowManager: WindowManager, + @Main private val mainHandler: Handler, + private val vibratorHelper: VibratorHelper, + private val configurationController: ConfigurationController, + latencyTracker: LatencyTracker +) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin { + + /** + * Injectable instance to create a new BackPanelController. + * + * Necessary because EdgeBackGestureHandler sometimes needs to create new instances of + * BackPanelController, and we need to match EdgeBackGestureHandler's context. + */ + class Factory @Inject constructor( + private val windowManager: WindowManager, + @Main private val mainHandler: Handler, + private val vibratorHelper: VibratorHelper, + private val configurationController: ConfigurationController, + private val latencyTracker: LatencyTracker + ) { + /** Construct a [BackPanelController]. */ + fun create(context: Context, backAnimation: BackAnimation?): BackPanelController { + val backPanelController = BackPanelController( + context, + backAnimation, + windowManager, + mainHandler, + vibratorHelper, + configurationController, + latencyTracker + ) + backPanelController.init() + return backPanelController + } + } + + private var params: EdgePanelParams = EdgePanelParams(resources) + private var currentState: GestureState = GestureState.GONE + private var previousState: GestureState = GestureState.GONE + + // Phone should only vibrate the first time the arrow is activated + private var hasHapticPlayed = false + + // Screen attributes + private lateinit var layoutParams: WindowManager.LayoutParams + private val displaySize = Point() + + private lateinit var backCallback: NavigationEdgeBackPlugin.BackCallback + + private var previousXTranslation = 0f + private var totalTouchDelta = 0f + private var velocityTracker: VelocityTracker? = null + set(value) { + if (field != value) field?.recycle() + field = value + } + get() { + if (field == null) field = VelocityTracker.obtain() + return field + } + + // The x,y position of the first touch event + private var startX = 0f + private var startY = 0f + + private val failsafeRunnable = Runnable { onFailsafe() } + + private enum class GestureState { + /* Arrow is off the screen and invisible */ + GONE, + + /* Arrow is animating in */ + ENTRY, + + /* could be entry, neutral, or stretched, releasing will commit back */ + ACTIVE, + + /* releasing will cancel back */ + INACTIVE, + + /* like committed, but animation takes longer */ + FLUNG, + + /* back action currently occurring, arrow soon to be GONE */ + COMMITTED, + + /* back action currently cancelling, arrow soon to be GONE */ + CANCELLED + } + + /** + * Wrapper around OnAnimationEndListener which runs the given runnable after a delay. The + * runnable is not called if the animation is cancelled + */ + class DelayedOnAnimationEndListener( + private val handler: Handler, + private val runnable: Runnable, + private val delay: Long + ) : DynamicAnimation.OnAnimationEndListener { + + override fun onAnimationEnd( + animation: DynamicAnimation<*>, + canceled: Boolean, + value: Float, + velocity: Float + ) { + animation.removeEndListener(this) + if (!canceled) { + handler.postDelayed(runnable, delay) + } + } + + fun runNow() { + runnable.run() + } + } + + private val setCommittedEndListener = + DelayedOnAnimationEndListener( + mainHandler, + { updateArrowState(GestureState.COMMITTED) }, + delay = FLING_PAUSE_DURATION_MS + ) + + private val setGoneEndListener = + DelayedOnAnimationEndListener( + mainHandler, + { + cancelFailsafe() + updateArrowState(GestureState.GONE) + }, + delay = 0 + ) + + // Vibration + private var vibrationTime: Long = 0 + + // Minimum size of the screen's width or height + private var screenSize = 0 + + /** + * Used for initialization and configuration changes + */ + private fun updateConfiguration() { + params.update(resources) + initializeBackAnimation() + mView.updateArrowPaint(params.arrowThickness) + } + + private val configurationListener = object : ConfigurationController.ConfigurationListener { + override fun onConfigChanged(newConfig: Configuration?) { + updateConfiguration() + } + + override fun onLayoutDirectionChanged(isLayoutRtl: Boolean) { + updateArrowDirection(isLayoutRtl) + } + } + + override fun onViewAttached() { + updateConfiguration() + updateArrowDirection(configurationController.isLayoutRtl) + updateArrowState(GestureState.GONE, force = true) + updateRestingArrowDimens(animated = false, currentState) + configurationController.addCallback(configurationListener) + } + + /** Update the arrow direction. The arrow should point the same way for both panels. */ + private fun updateArrowDirection(isLayoutRtl: Boolean) { + mView.arrowsPointLeft = isLayoutRtl + } + + override fun onViewDetached() { + configurationController.removeCallback(configurationListener) + } + + override fun onMotionEvent(event: MotionEvent) { + backAnimation?.onBackMotion( + event, + event.actionMasked, + if (mView.isLeftPanel) BackEvent.EDGE_LEFT else BackEvent.EDGE_RIGHT + ) + + velocityTracker!!.addMovement(event) + when (event.actionMasked) { + MotionEvent.ACTION_DOWN -> { + resetOnDown() + startX = event.x + startY = event.y + + // Reset the arrow to the side + updateArrowState(GestureState.ENTRY) + + windowManager.updateViewLayout(mView, layoutParams) + mView.startTrackingShowBackArrowLatency() + } + MotionEvent.ACTION_MOVE -> handleMoveEvent(event) + MotionEvent.ACTION_UP -> { + if (currentState == GestureState.ACTIVE) { + updateArrowState(if (isFlung()) GestureState.FLUNG else GestureState.COMMITTED) + } else { + updateArrowState(GestureState.CANCELLED) + } + velocityTracker = null + } + MotionEvent.ACTION_CANCEL -> { + updateArrowState(GestureState.CANCELLED) + velocityTracker = null + } + } + } + + private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) { + when (currentState) { + GestureState.GONE, GestureState.FLUNG, GestureState.COMMITTED, GestureState.CANCELLED -> + return + } + + updateArrowState( + when { + // Check if we should transition from ENTRY to ACTIVE + currentState == GestureState.ENTRY && xTranslation > params.swipeTriggerThreshold -> + GestureState.ACTIVE + + // Abort if we had continuous motion toward the edge for a while, OR the direction + // in Y is bigger than X * 2 + currentState == GestureState.ACTIVE && + ((totalTouchDelta < 0 && -totalTouchDelta > params.minDeltaForSwitch) || + (yTranslation > xTranslation * 2)) -> + GestureState.INACTIVE + + // Re-activate if we had continuous motion away from the edge for a while + currentState == GestureState.INACTIVE && + (totalTouchDelta > 0 && totalTouchDelta > params.minDeltaForSwitch) -> + GestureState.ACTIVE + + // By default assume the current direction is kept + else -> currentState + } + ) + } + + private fun handleMoveEvent(event: MotionEvent) { + when (currentState) { + GestureState.GONE, GestureState.FLUNG, GestureState.COMMITTED, + GestureState.CANCELLED -> return + } + + val x = event.x + val y = event.y + + val yOffset = y - startY + + // How far in the y direction we are from the original touch + val yTranslation = abs(yOffset) + + // How far in the x direction we are from the original touch ignoring motion that + // occurs between the screen edge and the touch start. + val xTranslation = max(0f, if (mView.isLeftPanel) x - startX else startX - x) + + // Compared to last time, how far we moved in the x direction. If <0, we are moving closer + // to the edge. If >0, we are moving further from the edge + val xDelta = xTranslation - previousXTranslation + previousXTranslation = xTranslation + + if (abs(xDelta) > 0) { + if (sign(xDelta) == sign(totalTouchDelta)) { + // Direction has NOT changed, so keep counting the delta + totalTouchDelta += xDelta + } else { + // Direction has changed, so reset the delta + totalTouchDelta = xDelta + } + } + + updateArrowStateOnMove(yTranslation, xTranslation) + when (currentState) { + GestureState.ACTIVE -> setActiveStretch(fullScreenStretchProgress(xTranslation)) + GestureState.ENTRY -> + setEntryStretch(preThresholdStretchProgress(xTranslation)) + GestureState.INACTIVE -> mView.resetStretch() + } + + // set y translation + setVerticalTranslation(yOffset) + } + + fun setVerticalTranslation(yOffset: Float) { + val yTranslation = abs(yOffset) + val maxYOffset = (mView.height / 2) - (params.entryBackgroundHeight / 2) + val yProgress = saturate(yTranslation / (maxYOffset * RUBBER_BAND_AMOUNT)) + mView.animateVertically( + RUBBER_BAND_INTERPOLATOR.getInterpolation(yProgress) * maxYOffset * sign( + yOffset + ) + ) + } + + /** + * @return the relative position of the drag from the time after the arrow is activated until + * the arrow is fully stretched (between 0.0 - 1.0f) + */ + fun fullScreenStretchProgress(xTranslation: Float): Float { + return saturate( + (xTranslation - params.swipeTriggerThreshold) / + (min( + params.fullyStretchedThreshold, + screenSize.toFloat() + ) - params.swipeTriggerThreshold) + ) + } + + /** + * Tracks the relative position of the drag from the entry until the threshold where the arrow + * activates (between 0.0 - 1.0f) + */ + fun preThresholdStretchProgress(xTranslation: Float): Float { + return saturate(xTranslation / params.swipeTriggerThreshold) + } + + fun setActiveStretch(progress: Float) { + val stretch = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress) + mView.setStretch( + arrowLengthStretch = stretch * (params.stretchedArrowLength - params.activeArrowLength), + arrowHeightStretch = stretch * (params.stretchedArrowHeight - params.activeArrowHeight), + backgroundWidthStretch = + stretch * (params.stretchBackgroundWidth - params.activeBackgroundWidth), + backgroundHeightStretch = + stretch * (params.stretchBackgroundHeight - params.activeBackgroundHeight), + backgroundEdgeCornerRadiusStretch = + stretch * (params.stretchEdgeCorners - params.activeEdgeCorners), + backgroundDragCornerRadiusStretch = + stretch * (params.stretchFarCorners - params.activeFarCorners), + horizontalTranslationStretch = stretch * (params.stretchMargin - params.activeMargin) + ) + } + + fun setEntryStretch(progress: Float) { + val bgStretch = ACCELERATE_INTERPOLATOR.getInterpolation(progress) + val arrowStretch = RUBBER_BAND_INTERPOLATOR.getInterpolation(progress) + mView.setStretch( + arrowLengthStretch = + arrowStretch * (params.activeArrowLength - params.entryArrowLength), + arrowHeightStretch = + arrowStretch * (params.activeArrowHeight - params.entryArrowHeight), + backgroundWidthStretch = + bgStretch * (params.preThresholdBackgroundWidth - params.entryBackgroundWidth), + backgroundHeightStretch = + bgStretch * (params.preThresholdBackgroundHeight - params.entryBackgroundHeight), + backgroundEdgeCornerRadiusStretch = + bgStretch * (params.preThresholdEdgeCorners - params.entryEdgeCorners), + backgroundDragCornerRadiusStretch = + bgStretch * (params.preThresholdFarCorners - params.entryFarCorners), + horizontalTranslationStretch = + bgStretch * (params.preThresholdMargin - params.entryMargin) + ) + } + + fun setBackAnimation(backAnimation: BackAnimation?) { + this.backAnimation = backAnimation + initializeBackAnimation() + } + + private fun initializeBackAnimation() { + backAnimation?.setSwipeThresholds( + params.swipeTriggerThreshold, + params.swipeProgressThreshold + ) + } + + override fun onDestroy() { + cancelFailsafe() + windowManager.removeView(mView) + } + + override fun setIsLeftPanel(isLeftPanel: Boolean) { + mView.isLeftPanel = isLeftPanel + layoutParams.gravity = if (isLeftPanel) { + Gravity.LEFT or Gravity.TOP + } else { + Gravity.RIGHT or Gravity.TOP + } + } + + override fun setInsets(insetLeft: Int, insetRight: Int) { + } + + override fun setBackCallback(callback: NavigationEdgeBackPlugin.BackCallback) { + backCallback = callback + } + + override fun setLayoutParams(layoutParams: WindowManager.LayoutParams) { + this.layoutParams = layoutParams + windowManager.addView(mView, layoutParams) + } + + private fun isFlung() = velocityTracker!!.run { + computeCurrentVelocity(1000) + abs(xVelocity) > MIN_FLING_VELOCITY + } + + private fun playFlingBackAnimation() { + playAnimation(setCommittedEndListener) + } + + private fun playCommitBackAnimation() { + // Check if we should vibrate again + if (previousState != GestureState.FLUNG) { + backCallback.triggerBack() + velocityTracker!!.computeCurrentVelocity(1000) + val isSlow = abs(velocityTracker!!.xVelocity) < 500 + val hasNotVibratedRecently = + SystemClock.uptimeMillis() - vibrationTime >= GESTURE_DURATION_FOR_CLICK_MS + if (isSlow || hasNotVibratedRecently) { + vibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK) + } + } + playAnimation(setGoneEndListener) + } + + private fun playCancelBackAnimation() { + backCallback.cancelBack() + playAnimation(setGoneEndListener) + } + + /** + * @return true if the animation is running, false otherwise. Some transitions don't animate + */ + private fun playAnimation(endListener: DelayedOnAnimationEndListener) { + updateRestingArrowDimens(animated = true, currentState) + + if (!mView.addEndListener(endListener)) { + scheduleFailsafe() + } + } + + private fun resetOnDown() { + hasHapticPlayed = false + totalTouchDelta = 0f + vibrationTime = 0 + cancelFailsafe() + backAnimation?.setTriggerBack(false) + } + + private fun updateYPosition(touchY: Float) { + var yPosition = touchY - params.fingerOffset + yPosition = Math.max(yPosition, params.minArrowYPosition.toFloat()) + yPosition -= layoutParams.height / 2.0f + layoutParams.y = constrain(yPosition.toInt(), 0, displaySize.y) + } + + override fun setDisplaySize(displaySize: Point) { + this.displaySize.set(displaySize.x, displaySize.y) + screenSize = Math.min(displaySize.x, displaySize.y) + } + + /** + * Updates resting arrow and background size not accounting for stretch + */ + private fun updateRestingArrowDimens(animated: Boolean, currentState: GestureState) { + mView.updateRestingArrowDimens( + backgroundWidth = + when (currentState) { + GestureState.GONE, GestureState.ENTRY -> params.entryBackgroundWidth + else -> params.activeBackgroundWidth + }, + backgroundHeight = + when (currentState) { + GestureState.GONE, GestureState.ENTRY -> params.entryBackgroundHeight + else -> params.activeBackgroundHeight + }, + backgroundEdgeCornerRadius = + when (currentState) { + GestureState.GONE, GestureState.ENTRY, GestureState.INACTIVE -> + params.entryEdgeCorners + else -> + params.activeEdgeCorners + }, + backgroundDragCornerRadius = + when (currentState) { + GestureState.GONE, GestureState.ENTRY -> params.entryFarCorners + else -> params.activeFarCorners + }, + arrowLength = + when (currentState) { + GestureState.ACTIVE, GestureState.INACTIVE, GestureState.COMMITTED, + GestureState.FLUNG -> params.activeArrowLength + GestureState.CANCELLED -> params.cancelledArrowLength + GestureState.GONE, GestureState.ENTRY -> params.entryArrowLength + }, + arrowHeight = + when (currentState) { + GestureState.ACTIVE, GestureState.INACTIVE, GestureState.COMMITTED, + GestureState.FLUNG -> params.activeArrowHeight + GestureState.CANCELLED -> params.cancelledArrowHeight + GestureState.GONE, GestureState.ENTRY -> params.entryArrowHeight + }, + horizontalTranslation = + when (currentState) { + GestureState.GONE -> -params.activeBackgroundWidth + // Position the cancelled/committed arrow slightly further off the screen so we + // do not see part of it bouncing + GestureState.CANCELLED, GestureState.COMMITTED -> + -params.activeBackgroundWidth * 1.5f + GestureState.FLUNG -> params.stretchMargin + GestureState.ACTIVE -> params.activeMargin + GestureState.ENTRY, GestureState.INACTIVE -> params.entryMargin + }, + animate = animated + ) + if (animated) { + when (currentState) { + GestureState.ENTRY, GestureState.ACTIVE, GestureState.FLUNG -> + mView.setArrowStiffness(ARROW_APPEAR_STIFFNESS, ARROW_APPEAR_DAMPING_RATIO) + else -> + mView.setArrowStiffness( + ARROW_DISAPPEAR_STIFFNESS, ARROW_DISAPPEAR_DAMPING_RATIO) + } + } + } + + /** + * Update arrow state. If state has not changed, this is a no-op. + * + * Transitioning to active/inactive will indicate whether or not releasing touch will trigger + * the back action. + */ + private fun updateArrowState(newState: GestureState, force: Boolean = false) { + if (!force && currentState == newState) return + + if (DEBUG) Log.d(TAG, "updateArrowState $currentState -> $newState") + previousState = currentState + currentState = newState + mView.visibility = if (currentState == GestureState.GONE) View.GONE else View.VISIBLE + + when (currentState) { + // Transitioning to GONE never animates since the arrow is (presumably) already off the + // screen + GestureState.GONE -> updateRestingArrowDimens(animated = false, currentState) + GestureState.ENTRY -> { + updateYPosition(startY) + updateRestingArrowDimens(animated = true, currentState) + } + GestureState.ACTIVE -> { + backAnimation?.setTriggerBack(true) + updateRestingArrowDimens(animated = true, currentState) + // Vibrate the first time we transition to ACTIVE + if (!hasHapticPlayed) { + hasHapticPlayed = true + vibrationTime = SystemClock.uptimeMillis() + vibratorHelper.vibrate(VibrationEffect.EFFECT_TICK) + } + } + GestureState.INACTIVE -> { + backAnimation?.setTriggerBack(false) + updateRestingArrowDimens(animated = true, currentState) + } + GestureState.FLUNG -> playFlingBackAnimation() + GestureState.COMMITTED -> playCommitBackAnimation() + GestureState.CANCELLED -> playCancelBackAnimation() + } + } + + private fun scheduleFailsafe() { + if (!ENABLE_FAILSAFE) return + cancelFailsafe() + if (DEBUG) Log.d(TAG, "scheduleFailsafe") + mainHandler.postDelayed(failsafeRunnable, FAILSAFE_DELAY_MS) + } + + private fun cancelFailsafe() { + if (DEBUG) Log.d(TAG, "cancelFailsafe") + mainHandler.removeCallbacks(failsafeRunnable) + } + + private fun onFailsafe() { + if (DEBUG) Log.d(TAG, "onFailsafe") + updateArrowState(GestureState.GONE, force = true) + } + + override fun dump(pw: PrintWriter) { + pw.println("$TAG:") + pw.println(" currentState=$currentState") + pw.println(" isLeftPanel=$mView.isLeftPanel") + } + + init { + if (DEBUG) mView.drawDebugInfo = { canvas -> + val debugStrings = listOf( + "$currentState", + "startX=$startX", + "startY=$startY", + "xDelta=${"%.1f".format(totalTouchDelta)}", + "xTranslation=${"%.1f".format(previousXTranslation)}", + "pre=${"%.0f".format(preThresholdStretchProgress(previousXTranslation) * 100)}%", + "post=${"%.0f".format(fullScreenStretchProgress(previousXTranslation) * 100)}%" + ) + val debugPaint = Paint().apply { + color = Color.WHITE + } + val debugInfoBottom = debugStrings.size * 32f + 4f + canvas.drawRect( + 4f, + 4f, + canvas.width.toFloat(), + debugStrings.size * 32f + 4f, + debugPaint + ) + debugPaint.apply { + color = Color.BLACK + textSize = 32f + } + var offset = 32f + for (debugText in debugStrings) { + canvas.drawText(debugText, 10f, offset, debugPaint) + offset += 32f + } + debugPaint.apply { + color = Color.RED + style = Paint.Style.STROKE + strokeWidth = 4f + } + val canvasWidth = canvas.width.toFloat() + val canvasHeight = canvas.height.toFloat() + canvas.drawRect(0f, 0f, canvasWidth, canvasHeight, debugPaint) + + fun drawVerticalLine(x: Float, color: Int) { + debugPaint.color = color + val x = if (mView.isLeftPanel) x else canvasWidth - x + canvas.drawLine(x, debugInfoBottom, x, canvas.height.toFloat(), debugPaint) + } + + drawVerticalLine(x = params.swipeTriggerThreshold, color = Color.BLUE) + drawVerticalLine(x = startX, color = Color.GREEN) + drawVerticalLine(x = previousXTranslation, color = Color.DKGRAY) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index ea41fe74f798..d41837b7cf4d 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -62,6 +62,8 @@ import com.android.systemui.R; import com.android.systemui.SystemUIFactory; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; @@ -182,6 +184,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private final PluginManager mPluginManager; private final ProtoTracer mProtoTracer; private final NavigationModeController mNavigationModeController; + private final BackPanelController.Factory mBackPanelControllerFactory; private final ViewConfiguration mViewConfiguration; private final WindowManager mWindowManager; private final IWindowManager mWindowManagerService; @@ -199,6 +202,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private final Region mExcludeRegion = new Region(); private final Region mUnrestrictedExcludeRegion = new Region(); private final LatencyTracker mLatencyTracker; + private final FeatureFlags mFeatureFlags; // The left side edge width where touch down is allowed private int mEdgeWidthLeft; @@ -230,6 +234,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private boolean mIsBackGestureAllowed; private boolean mGestureBlockingActivityRunning; private boolean mIsInPipMode; + private boolean mIsPredictiveBackAnimEnabled; private InputMonitor mInputMonitor; private InputChannelCompat.InputEventReceiver mInputEventReceiver; @@ -298,12 +303,22 @@ public class EdgeBackGestureHandler extends CurrentUserTracker }; - EdgeBackGestureHandler(Context context, OverviewProxyService overviewProxyService, - SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, - BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer, - NavigationModeController navigationModeController, ViewConfiguration viewConfiguration, - WindowManager windowManager, IWindowManager windowManagerService, - FalsingManager falsingManager, LatencyTracker latencyTracker) { + EdgeBackGestureHandler( + Context context, + OverviewProxyService overviewProxyService, + SysUiState sysUiState, + PluginManager pluginManager, + @Main Executor executor, + BroadcastDispatcher broadcastDispatcher, + ProtoTracer protoTracer, + NavigationModeController navigationModeController, + BackPanelController.Factory backPanelControllerFactory, + ViewConfiguration viewConfiguration, + WindowManager windowManager, + IWindowManager windowManagerService, + FalsingManager falsingManager, + LatencyTracker latencyTracker, + FeatureFlags featureFlags) { super(broadcastDispatcher); mContext = context; mDisplayId = context.getDisplayId(); @@ -313,11 +328,13 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mPluginManager = pluginManager; mProtoTracer = protoTracer; mNavigationModeController = navigationModeController; + mBackPanelControllerFactory = backPanelControllerFactory; mViewConfiguration = viewConfiguration; mWindowManager = windowManager; mWindowManagerService = windowManagerService; mFalsingManager = falsingManager; mLatencyTracker = latencyTracker; + mFeatureFlags = featureFlags; ComponentName recentsComponentName = ComponentName.unflattenFromString( context.getString(com.android.internal.R.string.config_recentsComponentName)); if (recentsComponentName != null) { @@ -507,8 +524,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker Choreographer.getInstance(), this::onInputEvent); // Add a nav bar panel window - setEdgeBackPlugin( - new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker)); + mIsPredictiveBackAnimEnabled = + mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_ANIM); + resetEdgeBackPlugin(); mPluginManager.addPluginListener( this, NavigationEdgeBackPlugin.class, /*allowMultiple=*/ false); } @@ -523,7 +541,17 @@ public class EdgeBackGestureHandler extends CurrentUserTracker @Override public void onPluginDisconnected(NavigationEdgeBackPlugin plugin) { - setEdgeBackPlugin(new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker)); + resetEdgeBackPlugin(); + } + + private void resetEdgeBackPlugin() { + if (mIsPredictiveBackAnimEnabled) { + setEdgeBackPlugin( + mBackPanelControllerFactory.create(mContext, mBackAnimation)); + } else { + setEdgeBackPlugin( + new NavigationBarEdgePanel(mContext, mBackAnimation, mLatencyTracker)); + } } private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) { @@ -948,8 +976,12 @@ public class EdgeBackGestureHandler extends CurrentUserTracker public void setBackAnimation(BackAnimation backAnimation) { mBackAnimation = backAnimation; - if (mEdgeBackPlugin != null && mEdgeBackPlugin instanceof NavigationBarEdgePanel) { - ((NavigationBarEdgePanel) mEdgeBackPlugin).setBackAnimation(backAnimation); + if (mEdgeBackPlugin != null) { + if (mEdgeBackPlugin instanceof NavigationBarEdgePanel) { + ((NavigationBarEdgePanel) mEdgeBackPlugin).setBackAnimation(backAnimation); + } else if (mEdgeBackPlugin instanceof BackPanelController) { + ((BackPanelController) mEdgeBackPlugin).setBackAnimation(backAnimation); + } } } @@ -967,20 +999,29 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private final BroadcastDispatcher mBroadcastDispatcher; private final ProtoTracer mProtoTracer; private final NavigationModeController mNavigationModeController; + private final BackPanelController.Factory mBackPanelControllerFactory; private final ViewConfiguration mViewConfiguration; private final WindowManager mWindowManager; private final IWindowManager mWindowManagerService; private final FalsingManager mFalsingManager; private final LatencyTracker mLatencyTracker; + private final FeatureFlags mFeatureFlags; @Inject public Factory(OverviewProxyService overviewProxyService, - SysUiState sysUiState, PluginManager pluginManager, @Main Executor executor, - BroadcastDispatcher broadcastDispatcher, ProtoTracer protoTracer, - NavigationModeController navigationModeController, - ViewConfiguration viewConfiguration, WindowManager windowManager, - IWindowManager windowManagerService, FalsingManager falsingManager, - LatencyTracker latencyTracker) { + SysUiState sysUiState, + PluginManager pluginManager, + @Main Executor executor, + BroadcastDispatcher broadcastDispatcher, + ProtoTracer protoTracer, + NavigationModeController navigationModeController, + BackPanelController.Factory backPanelControllerFactory, + ViewConfiguration viewConfiguration, + WindowManager windowManager, + IWindowManager windowManagerService, + FalsingManager falsingManager, + LatencyTracker latencyTracker, + FeatureFlags featureFlags) { mOverviewProxyService = overviewProxyService; mSysUiState = sysUiState; mPluginManager = pluginManager; @@ -988,19 +1029,33 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mBroadcastDispatcher = broadcastDispatcher; mProtoTracer = protoTracer; mNavigationModeController = navigationModeController; + mBackPanelControllerFactory = backPanelControllerFactory; mViewConfiguration = viewConfiguration; mWindowManager = windowManager; mWindowManagerService = windowManagerService; mFalsingManager = falsingManager; mLatencyTracker = latencyTracker; + mFeatureFlags = featureFlags; } /** Construct a {@link EdgeBackGestureHandler}. */ public EdgeBackGestureHandler create(Context context) { - return new EdgeBackGestureHandler(context, mOverviewProxyService, mSysUiState, - mPluginManager, mExecutor, mBroadcastDispatcher, mProtoTracer, - mNavigationModeController, mViewConfiguration, mWindowManager, - mWindowManagerService, mFalsingManager, mLatencyTracker); + return new EdgeBackGestureHandler( + context, + mOverviewProxyService, + mSysUiState, + mPluginManager, + mExecutor, + mBroadcastDispatcher, + mProtoTracer, + mNavigationModeController, + mBackPanelControllerFactory, + mViewConfiguration, + mWindowManager, + mWindowManagerService, + mFalsingManager, + mLatencyTracker, + mFeatureFlags); } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt new file mode 100644 index 000000000000..51566b0f8393 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt @@ -0,0 +1,139 @@ +package com.android.systemui.navigationbar.gestural + +import android.content.res.Resources +import com.android.systemui.R + +data class EdgePanelParams(private var resources: Resources) { + var arrowThickness: Float = 0f + private set + var entryArrowLength: Float = 0f + private set + var entryArrowHeight: Float = 0f + private set + var activeArrowLength: Float = 0f + private set + var activeArrowHeight: Float = 0f + private set + var stretchedArrowLength: Float = 0f + private set + var stretchedArrowHeight: Float = 0f + private set + var cancelledArrowLength: Float = 0f + private set + var cancelledArrowHeight: Float = 0f + private set + var entryMargin: Float = 0f + private set + var entryBackgroundWidth: Float = 0f + private set + var entryBackgroundHeight: Float = 0f + private set + var entryEdgeCorners: Float = 0f + private set + var entryFarCorners: Float = 0f + private set + var preThresholdMargin: Float = 0f + private set + var preThresholdBackgroundWidth: Float = 0f + private set + var preThresholdBackgroundHeight: Float = 0f + private set + var preThresholdEdgeCorners: Float = 0f + private set + var preThresholdFarCorners: Float = 0f + private set + var activeMargin: Float = 0f + private set + var activeBackgroundWidth: Float = 0f + private set + var activeBackgroundHeight: Float = 0f + private set + var activeEdgeCorners: Float = 0f + private set + var activeFarCorners: Float = 0f + private set + var fullyStretchedThreshold: Float = 0f + private set + var stretchMargin: Float = 0f + private set + var stretchBackgroundWidth: Float = 0f + private set + var stretchBackgroundHeight: Float = 0f + private set + var stretchEdgeCorners: Float = 0f + private set + var stretchFarCorners: Float = 0f + private set + + // navigation bar edge constants + var arrowPaddingEnd: Int = 0 + private set + + // The closest to y + var minArrowYPosition: Int = 0 + private set + var fingerOffset: Int = 0 + private set + var swipeTriggerThreshold: Float = 0f + private set + var swipeProgressThreshold: Float = 0f + private set + + // The minimum delta needed to change direction / stop triggering back + var minDeltaForSwitch: Int = 0 + private set + + init { + update(resources) + } + + private fun getDimen(id: Int): Float { + return resources.getDimension(id) + } + + private fun getPx(id: Int): Int { + return resources.getDimensionPixelSize(id) + } + + fun update(resources: Resources) { + this.resources = resources + arrowThickness = getDimen(R.dimen.navigation_edge_arrow_thickness) + entryArrowLength = getDimen(R.dimen.navigation_edge_entry_arrow_length) + entryArrowHeight = getDimen(R.dimen.navigation_edge_entry_arrow_height) + activeArrowLength = getDimen(R.dimen.navigation_edge_active_arrow_length) + activeArrowHeight = getDimen(R.dimen.navigation_edge_active_arrow_height) + stretchedArrowLength = getDimen(R.dimen.navigation_edge_stretched_arrow_length) + stretchedArrowHeight = getDimen(R.dimen.navigation_edge_stretched_arrow_height) + cancelledArrowLength = getDimen(R.dimen.navigation_edge_cancelled_arrow_length) + cancelledArrowHeight = getDimen(R.dimen.navigation_edge_cancelled_arrow_height) + entryMargin = getDimen(R.dimen.navigation_edge_entry_margin) + entryBackgroundWidth = getDimen(R.dimen.navigation_edge_entry_background_width) + entryBackgroundHeight = getDimen(R.dimen.navigation_edge_entry_background_height) + entryEdgeCorners = getDimen(R.dimen.navigation_edge_entry_edge_corners) + entryFarCorners = getDimen(R.dimen.navigation_edge_entry_far_corners) + preThresholdMargin = getDimen(R.dimen.navigation_edge_pre_threshold_margin) + preThresholdBackgroundWidth = + getDimen(R.dimen.navigation_edge_pre_threshold_background_width) + preThresholdBackgroundHeight = + getDimen(R.dimen.navigation_edge_pre_threshold_background_height) + preThresholdEdgeCorners = getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners) + preThresholdFarCorners = getDimen(R.dimen.navigation_edge_pre_threshold_far_corners) + activeMargin = getDimen(R.dimen.navigation_edge_active_margin) + activeBackgroundWidth = getDimen(R.dimen.navigation_edge_active_background_width) + activeBackgroundHeight = getDimen(R.dimen.navigation_edge_active_background_height) + activeEdgeCorners = getDimen(R.dimen.navigation_edge_active_edge_corners) + activeFarCorners = getDimen(R.dimen.navigation_edge_active_far_corners) + fullyStretchedThreshold = getDimen(R.dimen.navigation_edge_stretch_threshold) + stretchMargin = getDimen(R.dimen.navigation_edge_stretch_margin) + stretchBackgroundWidth = getDimen(R.dimen.navigation_edge_stretch_background_width) + stretchBackgroundHeight = getDimen(R.dimen.navigation_edge_stretch_background_height) + stretchEdgeCorners = getDimen(R.dimen.navigation_edge_stretch_left_corners) + stretchFarCorners = getDimen(R.dimen.navigation_edge_stretch_right_corners) + arrowPaddingEnd = getPx(R.dimen.navigation_edge_panel_padding) + minArrowYPosition = getPx(R.dimen.navigation_edge_arrow_min_y) + fingerOffset = getPx(R.dimen.navigation_edge_finger_offset) + swipeTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold) + swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold) + minDeltaForSwitch = getPx(R.dimen.navigation_edge_minimum_x_delta_for_switch) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java index 1a7bd8cb6cf9..18be0aa6580d 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/PeopleSpaceWidgetManager.java @@ -289,7 +289,7 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "Updating widget: " + appWidgetId); PeopleSpaceTile tile = getTileForExistingWidget(appWidgetId); if (tile == null) { - Log.e(TAG, "Matching conversation not found for shortcut ID"); + Log.e(TAG, "Matching conversation not found for widget " + appWidgetId); } updateAppWidgetOptionsAndView(appWidgetId, tile); widgetIdToTile.put(appWidgetId, tile); @@ -308,7 +308,7 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "Widget: " + appWidgetId + " for: " + key.toString()); if (!PeopleTileKey.isValid(key)) { - Log.e(TAG, "Cannot update invalid widget"); + Log.e(TAG, "Invalid tile key updating widget " + appWidgetId); return; } RemoteViews views = PeopleTileViewHelper.createRemoteViews(mContext, tile, appWidgetId, @@ -330,7 +330,7 @@ public class PeopleSpaceWidgetManager { /** Updates tile in app widget options and the current view. */ public void updateAppWidgetOptionsAndView(int appWidgetId, PeopleSpaceTile tile) { if (tile == null) { - if (DEBUG) Log.w(TAG, "Storing null tile"); + Log.w(TAG, "Storing null tile for widget " + appWidgetId); } synchronized (mTiles) { mTiles.put(appWidgetId, tile); @@ -348,7 +348,7 @@ public class PeopleSpaceWidgetManager { try { return getTileForExistingWidgetThrowing(appWidgetId); } catch (Exception e) { - Log.e(TAG, "failed to retrieve tile for widget ID " + appWidgetId, e); + Log.e(TAG, "failed to retrieve tile for existing widget " + appWidgetId, e); return null; } } @@ -388,7 +388,7 @@ public class PeopleSpaceWidgetManager { boolean supplementFromStorage) throws PackageManager.NameNotFoundException { if (!PeopleTileKey.isValid(key)) { - Log.e(TAG, "PeopleTileKey invalid: " + key.toString()); + Log.e(TAG, "Invalid tile key finding tile for existing widget " + appWidgetId); return null; } @@ -423,7 +423,7 @@ public class PeopleSpaceWidgetManager { // Add current state. return getTileWithCurrentState(storedTile.build(), ACTION_BOOT_COMPLETED); } catch (RemoteException e) { - Log.e(TAG, "getTileFromPersistentStorage failing", e); + Log.e(TAG, "getTileFromPersistentStorage failing for widget " + appWidgetId, e); return null; } } @@ -591,10 +591,7 @@ public class PeopleSpaceWidgetManager { if (DEBUG) Log.d(TAG, "Augmenting tile for existing widget: " + widgetId); PeopleSpaceTile tile = getTileForExistingWidget(widgetId); if (tile == null) { - if (DEBUG) { - Log.w(TAG, "Widget: " + widgetId - + ". Null tile for existing widget, skipping update."); - } + Log.w(TAG, "Null tile for existing widget " + widgetId + ", skipping update."); return Optional.empty(); } String contactUriString = mSharedPrefs.getString(String.valueOf(widgetId), null); @@ -816,7 +813,7 @@ public class PeopleSpaceWidgetManager { tile = getTileFromPersistentStorage(key, appWidgetId, /* supplementFromStorage= */ false); } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "Cannot add widget since app was uninstalled"); + Log.e(TAG, "Cannot add widget " + appWidgetId + " since app was uninstalled"); return; } if (tile == null) { @@ -851,7 +848,7 @@ public class PeopleSpaceWidgetManager { Collections.singletonList(tile.getId()), tile.getUserHandle(), LauncherApps.FLAG_CACHE_PEOPLE_TILE_SHORTCUTS); } catch (Exception e) { - Log.w(TAG, "failed to cache shortcut", e); + Log.w(TAG, "failed to cache shortcut for widget " + appWidgetId, e); } PeopleSpaceTile finalTile = tile; mBgExecutor.execute( @@ -862,7 +859,7 @@ public class PeopleSpaceWidgetManager { public void registerConversationListenerIfNeeded(int widgetId, PeopleTileKey key) { // Retrieve storage needed for registration. if (!PeopleTileKey.isValid(key)) { - if (DEBUG) Log.w(TAG, "Could not register listener for widget: " + widgetId); + Log.w(TAG, "Invalid tile key registering listener for widget " + widgetId); return; } TileConversationListener newListener = new TileConversationListener(); @@ -911,7 +908,7 @@ public class PeopleSpaceWidgetManager { widgetSp.getInt(USER_ID, INVALID_USER_ID), widgetSp.getString(PACKAGE_NAME, null)); if (!PeopleTileKey.isValid(key)) { - if (DEBUG) Log.e(TAG, "Could not delete " + widgetId); + Log.e(TAG, "Invalid tile key trying to remove widget " + widgetId); return; } storedWidgetIdsForKey = new HashSet<>( @@ -1083,7 +1080,8 @@ public class PeopleSpaceWidgetManager { synchronized (mLock) { existingTile = getTileForExistingWidgetThrowing(appWidgetId); if (existingTile == null) { - Log.e(TAG, "Matching conversation not found for shortcut ID"); + Log.e(TAG, "Matching conversation not found for widget " + + appWidgetId); continue; } updatedTile = getTileWithCurrentState(existingTile, entryPoint); @@ -1091,7 +1089,7 @@ public class PeopleSpaceWidgetManager { } } catch (PackageManager.NameNotFoundException e) { // Delete data for uninstalled widgets. - Log.e(TAG, "package no longer found for tile", e); + Log.e(TAG, "Package no longer found for widget " + appWidgetId, e); JobScheduler jobScheduler = mContext.getSystemService(JobScheduler.class); if (jobScheduler != null && jobScheduler.getPendingJob(PeopleBackupFollowUpJob.JOB_ID) != null) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java index ba6b1dd28c97..875493d73f9e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java @@ -17,6 +17,7 @@ package com.android.systemui.qs; import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FGS_MANAGER_FOOTER_VIEW; +import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; import android.content.Context; import android.view.View; @@ -141,8 +142,8 @@ public class QSFgsManagerFooter implements View.OnClickListener, public void handleRefreshState() { mMainExecutor.execute(() -> { - CharSequence text = mContext.getResources().getQuantityString( - R.plurals.fgs_manager_footer_label, mNumPackages, mNumPackages); + CharSequence text = icuMessageFormat(mContext.getResources(), + R.string.fgs_manager_footer_label, mNumPackages); mFooterText.setText(text); mNumberView.setText(Integer.toString(mNumPackages)); mNumberView.setContentDescription(text); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index d03a2e55d628..8c5e6cc63147 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -57,7 +57,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler; import com.android.systemui.util.LifecycleFragment; -import com.android.systemui.util.Utils; import java.io.PrintWriter; import java.util.Arrays; @@ -624,7 +623,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca if (mQSAnimator != null) { mQSAnimator.setPosition(expansion); } - updateMediaPositions(); } private void setAlphaAnimationProgress(float progress) { @@ -644,10 +642,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca if (mLastQSExpansion == 1.0f) { // Fully expanded, let's set the layout bounds as clip bounds. This is necessary because // it's a scrollview and otherwise wouldn't be clipped. However, we set the horizontal - // bounds so the pages go to the ends of QSContainerImpl - ViewGroup.MarginLayoutParams lp = - (ViewGroup.MarginLayoutParams) mQSPanelScrollView.getLayoutParams(); - mQsBounds.set(-lp.leftMargin, 0, mQSPanelScrollView.getWidth() + lp.rightMargin, + // bounds so the pages go to the ends of QSContainerImpl (most cases) or its parent + // (large screen portrait) + int sideMargin = getResources().getDimensionPixelSize( + R.dimen.qs_tiles_page_horizontal_margin) * 2; + mQsBounds.set(-sideMargin, 0, mQSPanelScrollView.getWidth() + sideMargin, mQSPanelScrollView.getHeight()); } mQSPanelScrollView.setClipBounds(mQsBounds); @@ -660,54 +659,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca - mQSPanelScrollView.getPaddingBottom()); } - private void updateMediaPositions() { - if (Utils.useQsMediaPlayer(getContext())) { - mContainer.getLocationOnScreen(mTmpLocation); - float absoluteBottomPosition = mTmpLocation[1] + mContainer.getHeight(); - // The Media can be scrolled off screen by default, let's offset it - float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY() - + mQSPanelScrollView.getScrollRange(); - pinToBottom(expandedMediaPosition, mQsMediaHost, true /* expanded */); - // The expanded media host should never move above the laid out position - pinToBottom(absoluteBottomPosition, mQqsMediaHost, false /* expanded */); - } - } - - private void pinToBottom(float absoluteBottomPosition, MediaHost mediaHost, boolean expanded) { - View hostView = mediaHost.getHostView(); - // On keyguard we cross-fade to expanded, so no need to pin it. - // If the collapsed qs isn't visible, we also just keep it at the laid out position. - if (mLastQSExpansion > 0 && !isKeyguardState() && mQqsMediaHost.getVisible()) { - float targetPosition = absoluteBottomPosition - getTotalBottomMargin(hostView) - - hostView.getHeight(); - float currentPosition = mediaHost.getCurrentBounds().top - - hostView.getTranslationY(); - float translationY = targetPosition - currentPosition; - if (expanded) { - // Never go below the laid out position. This is necessary since the qs panel can - // change in height and we don't want to ever go below it's position - translationY = Math.min(translationY, 0); - } else { - translationY = Math.max(translationY, 0); - } - hostView.setTranslationY(translationY); - } else { - hostView.setTranslationY(0); - } - } - - private float getTotalBottomMargin(View startView) { - int result = 0; - View child = startView; - View parent = (View) startView.getParent(); - while (!(parent instanceof QSContainerImpl) && parent != null) { - result += parent.getHeight() - child.getBottom(); - child = parent; - parent = (View) parent.getParent(); - } - return result; - } - private boolean headerWillBeAnimating() { return mState == StatusBarState.KEYGUARD && mShowCollapsedOnKeyguard && !isKeyguardState(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 1488231275c3..a92c7e3c8554 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -39,6 +39,7 @@ import com.android.systemui.qs.tiles.ColorInversionTile; import com.android.systemui.qs.tiles.DataSaverTile; import com.android.systemui.qs.tiles.DeviceControlsTile; import com.android.systemui.qs.tiles.DndTile; +import com.android.systemui.qs.tiles.DreamTile; import com.android.systemui.qs.tiles.FlashlightTile; import com.android.systemui.qs.tiles.HotspotTile; import com.android.systemui.qs.tiles.InternetTile; @@ -96,6 +97,7 @@ public class QSFactoryImpl implements QSFactory { private final Provider<QuickAccessWalletTile> mQuickAccessWalletTileProvider; private final Provider<QRCodeScannerTile> mQRCodeScannerTileProvider; private final Provider<OneHandedModeTile> mOneHandedModeTileProvider; + private final Provider<DreamTile> mDreamTileProvider; private final Lazy<QSHost> mQsHostLazy; private final Provider<CustomTile.Builder> mCustomTileBuilderProvider; @@ -132,7 +134,8 @@ public class QSFactoryImpl implements QSFactory { Provider<QuickAccessWalletTile> quickAccessWalletTileProvider, Provider<QRCodeScannerTile> qrCodeScannerTileProvider, Provider<OneHandedModeTile> oneHandedModeTileProvider, - Provider<ColorCorrectionTile> colorCorrectionTileProvider) { + Provider<ColorCorrectionTile> colorCorrectionTileProvider, + Provider<DreamTile> dreamTileProvider) { mQsHostLazy = qsHostLazy; mCustomTileBuilderProvider = customTileBuilderProvider; @@ -165,6 +168,7 @@ public class QSFactoryImpl implements QSFactory { mQRCodeScannerTileProvider = qrCodeScannerTileProvider; mOneHandedModeTileProvider = oneHandedModeTileProvider; mColorCorrectionTileProvider = colorCorrectionTileProvider; + mDreamTileProvider = dreamTileProvider; } /** Creates a tile with a type based on {@code tileSpec} */ @@ -238,6 +242,8 @@ public class QSFactoryImpl implements QSFactory { return mOneHandedModeTileProvider.get(); case "color_correction": return mColorCorrectionTileProvider.get(); + case "dream": + return mDreamTileProvider.get(); } // Custom tiles diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt index 59164dea9c45..5147d5934039 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -670,7 +670,8 @@ internal object SubtitleArrayMapping { "qr_code_scanner" to R.array.tile_states_qr_code_scanner, "alarm" to R.array.tile_states_alarm, "onehanded" to R.array.tile_states_onehanded, - "color_correction" to R.array.tile_states_color_correction + "color_correction" to R.array.tile_states_color_correction, + "dream" to R.array.tile_states_dream ) fun getSubtitleId(spec: String?): Int { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index f736231bc22b..9a0d0d9656ca 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -16,12 +16,12 @@ package com.android.systemui.qs.tiles; +import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; + import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; -import android.content.Context; import android.content.Intent; -import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.os.UserManager; @@ -134,9 +134,10 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_bluetooth); state.stateDescription = ""; + if (enabled) { if (connected) { - state.icon = new BluetoothConnectedTileIcon(); + state.icon = ResourceIcon.get(R.drawable.qs_bluetooth_icon_on); if (!TextUtils.isEmpty(mController.getConnectedDeviceName())) { state.label = mController.getConnectedDeviceName(); } @@ -145,21 +146,19 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { + ", " + state.secondaryLabel; } else if (state.isTransient) { state.icon = ResourceIcon.get( - com.android.internal.R.drawable.ic_bluetooth_transient_animation); + R.drawable.qs_bluetooth_icon_search); state.stateDescription = state.secondaryLabel; } else { state.icon = - ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth); + ResourceIcon.get(R.drawable.qs_bluetooth_icon_off); state.stateDescription = mContext.getString(R.string.accessibility_not_connected); } state.state = Tile.STATE_ACTIVE; } else { - state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_bluetooth); + state.icon = ResourceIcon.get(R.drawable.qs_bluetooth_icon_off); state.state = Tile.STATE_INACTIVE; } - state.dualLabelContentDescription = mContext.getResources().getString( - R.string.accessibility_quick_settings_open_settings, getTileLabel()); state.expandedAccessibilityClassName = Switch.class.getName(); } @@ -185,10 +184,8 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { List<CachedBluetoothDevice> connectedDevices = mController.getConnectedDevices(); if (enabled && connected && !connectedDevices.isEmpty()) { if (connectedDevices.size() > 1) { - // TODO(b/76102598): add a new string for "X connected devices" after P - return mContext.getResources().getQuantityString( - R.plurals.quick_settings_hotspot_secondary_label_num_devices, - connectedDevices.size(), + return icuMessageFormat(mContext.getResources(), + R.string.quick_settings_hotspot_secondary_label_num_devices, connectedDevices.size()); } @@ -244,22 +241,4 @@ public class BluetoothTile extends QSTileImpl<BooleanState> { refreshState(); } }; - - /** - * Bluetooth icon wrapper (when connected with no battery indicator) for Quick Settings. This is - * used instead of {@link com.android.systemui.qs.tileimpl.QSTileImpl.DrawableIcon} in order to - * use a context that reflects dark/light theme attributes. - */ - private class BluetoothConnectedTileIcon extends Icon { - - BluetoothConnectedTileIcon() { - // Do nothing. Default constructor to limit visibility. - } - - @Override - public Drawable getDrawable(Context context) { - // This method returns Pair<Drawable, String> - the first value is the drawable. - return context.getDrawable(R.drawable.ic_bluetooth_connected); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java new file mode 100644 index 000000000000..22e725b41a64 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DreamTile.java @@ -0,0 +1,244 @@ +/* + * Copyright (C) 2022 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.qs.tiles; + +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; +import android.os.Build; +import android.os.Handler; +import android.os.Looper; +import android.os.RemoteException; +import android.provider.Settings; +import android.service.dreams.IDreamManager; +import android.service.quicksettings.Tile; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; + +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.R; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dreams.dagger.DreamModule; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.SettingObserver; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.util.settings.SecureSettings; + +import javax.inject.Inject; +import javax.inject.Named; + +/** Quick settings tile: Screensaver (dream) **/ +public class DreamTile extends QSTileImpl<QSTile.BooleanState> { + + private static final String LOG_TAG = "QSDream"; + private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_screen_saver); + private final IDreamManager mDreamManager; + private final BroadcastDispatcher mBroadcastDispatcher; + private final SettingObserver mEnabledSettingObserver; + private final SettingObserver mDreamSettingObserver; + private final UserTracker mUserTracker; + private final boolean mDreamSupported; + private final boolean mDreamOnlyEnabledForSystemUser; + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + refreshState(); + } + }; + + @Inject + public DreamTile( + QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + FalsingManager falsingManager, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + IDreamManager dreamManager, + SecureSettings secureSettings, + BroadcastDispatcher broadcastDispatcher, + UserTracker userTracker, + @Named(DreamModule.DREAM_SUPPORTED) boolean dreamSupported, + @Named(DreamModule.DREAM_ONLY_ENABLED_FOR_SYSTEM_USER) + boolean dreamOnlyEnabledForSystemUser + ) { + super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger, + statusBarStateController, activityStarter, qsLogger); + mDreamManager = dreamManager; + mBroadcastDispatcher = broadcastDispatcher; + mEnabledSettingObserver = new SettingObserver(secureSettings, mHandler, + Settings.Secure.SCREENSAVER_ENABLED) { + @Override + protected void handleValueChanged(int value, boolean observedChange) { + refreshState(); + } + }; + mDreamSettingObserver = new SettingObserver(secureSettings, mHandler, + Settings.Secure.SCREENSAVER_COMPONENTS) { + @Override + protected void handleValueChanged(int value, boolean observedChange) { + refreshState(); + } + }; + mUserTracker = userTracker; + mDreamSupported = dreamSupported; + mDreamOnlyEnabledForSystemUser = dreamOnlyEnabledForSystemUser; + } + + @Override + public void handleSetListening(boolean listening) { + super.handleSetListening(listening); + + if (listening) { + final IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_DREAMING_STARTED); + filter.addAction(Intent.ACTION_DREAMING_STOPPED); + mBroadcastDispatcher.registerReceiver(mReceiver, filter); + } else { + mBroadcastDispatcher.unregisterReceiver(mReceiver); + } + mEnabledSettingObserver.setListening(listening); + mDreamSettingObserver.setListening(listening); + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + protected void handleClick(@Nullable View view) { + try { + if (mDreamManager.isDreaming()) { + mDreamManager.awaken(); + } else { + mDreamManager.dream(); + } + } catch (RemoteException e) { + Log.e(LOG_TAG, "Can't dream", e); + } + } + + @Override + protected void handleLongClick(@Nullable View view) { + try { + // Need to wake on long click so bouncer->settings works. + mDreamManager.awaken(); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Can't awaken", e); + } + super.handleLongClick(view); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + state.label = getTileLabel(); + state.secondaryLabel = getActiveDreamName(); + state.contentDescription = getContentDescription(state.secondaryLabel); + state.icon = mIcon; + + if (getActiveDream() == null || !isScreensaverEnabled()) { + state.state = Tile.STATE_UNAVAILABLE; + } else { + state.state = isDreaming() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + } + } + + @Nullable + @Override + public Intent getLongClickIntent() { + return new Intent(Settings.ACTION_DREAM_SETTINGS); + } + + @Override + public CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_screensaver_label); + } + + @Override + public boolean isAvailable() { + // Only enable for devices that have dreams for the user(s) that can dream. + // For now, restrict to debug users. + return Build.isDebuggable() + && mDreamSupported + && (!mDreamOnlyEnabledForSystemUser || mUserTracker.getUserHandle().isSystem()); + } + + @VisibleForTesting + protected CharSequence getContentDescription(CharSequence dreamName) { + return !TextUtils.isEmpty(dreamName) + ? getTileLabel() + ", " + dreamName : getTileLabel(); + } + + private boolean isDreaming() { + try { + return mDreamManager.isDreaming(); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Can't check if dreaming", e); + return false; + } + } + + private ComponentName getActiveDream() { + try { + final ComponentName[] dreams = mDreamManager.getDreamComponents(); + return dreams != null && dreams.length > 0 ? dreams[0] : null; + } catch (RemoteException e) { + Log.w(TAG, "Failed to get active dream", e); + return null; + } + } + + private CharSequence getActiveDreamName() { + final ComponentName componentName = getActiveDream(); + if (componentName != null) { + PackageManager pm = mContext.getPackageManager(); + try { + ServiceInfo ri = pm.getServiceInfo(componentName, 0); + if (ri != null) { + return ri.loadLabel(pm); + } + } catch (PackageManager.NameNotFoundException exc) { + return null; // uninstalled? + } + } + return null; + } + + private boolean isScreensaverEnabled() { + return mEnabledSettingObserver.getValue() == 1; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 7c8e77b5d993..b6f6e933bf84 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -16,6 +16,8 @@ package com.android.systemui.qs.tiles; +import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; + import android.content.Intent; import android.os.Handler; import android.os.Looper; @@ -186,9 +188,8 @@ public class HotspotTile extends QSTileImpl<BooleanState> { return mContext.getString( R.string.quick_settings_hotspot_secondary_label_data_saver_enabled); } else if (numConnectedDevices > 0 && isActive) { - return mContext.getResources().getQuantityString( - R.plurals.quick_settings_hotspot_secondary_label_num_devices, - numConnectedDevices, + return icuMessageFormat(mContext.getResources(), + R.string.quick_settings_hotspot_secondary_label_num_devices, numConnectedDevices); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index 177c82ed3d78..600874f0d01a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -57,6 +57,7 @@ import javax.inject.Inject; /** Quick settings tile: Rotation **/ public class RotationLockTile extends QSTileImpl<BooleanState> implements BatteryController.BatteryStateChangeCallback { + private static final String EMPTY_SECONDARY_STRING = ""; private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_auto_rotate); private final RotationLockController mController; @@ -144,13 +145,15 @@ public class RotationLockTile extends QSTileImpl<BooleanState> implements && mController.isCameraRotationEnabled(); state.value = !rotationLocked; state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label); - state.icon = mIcon; + state.icon = ResourceIcon.get(R.drawable.qs_auto_rotate_icon_off); state.contentDescription = getAccessibilityString(rotationLocked); - if (!rotationLocked && cameraRotation) { - state.secondaryLabel = mContext.getResources().getString( - R.string.rotation_lock_camera_rotation_on); + if (!rotationLocked) { + state.secondaryLabel = cameraRotation ? mContext.getResources().getString( + R.string.rotation_lock_camera_rotation_on) + : EMPTY_SECONDARY_STRING; + state.icon = ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on); } else { - state.secondaryLabel = ""; + state.secondaryLabel = EMPTY_SECONDARY_STRING; } state.stateDescription = state.secondaryLabel; diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 9768e706764f..da943d2ea594 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -736,7 +736,7 @@ public class OverviewProxyService extends CurrentUserTracker implements } private void onStatusBarStateChanged(boolean keyguardShowing, boolean keyguardOccluded, - boolean bouncerShowing, boolean isDozing) { + boolean bouncerShowing, boolean isDozing, boolean panelExpanded) { mSysUiState.setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING, keyguardShowing && !keyguardOccluded) .setFlag(SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index d71dec8ff396..5cdd01f0ee0b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -68,6 +68,7 @@ import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.policy.RemoteInputView; import com.android.systemui.util.DumpUtilsKt; +import com.android.systemui.util.ListenerSet; import java.io.PrintWriter; import java.util.ArrayList; @@ -75,6 +76,7 @@ import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import dagger.Lazy; @@ -118,6 +120,8 @@ public class NotificationRemoteInputManager implements Dumpable { protected Callback mCallback; private final List<RemoteInputController.Callback> mControllerCallbacks = new ArrayList<>(); + private final ListenerSet<Consumer<NotificationEntry>> mActionPressListeners = + new ListenerSet<>(); private final InteractionHandler mInteractionHandler = new InteractionHandler() { @@ -401,6 +405,14 @@ public class NotificationRemoteInputManager implements Dumpable { } } + public void addActionPressListener(Consumer<NotificationEntry> listener) { + mActionPressListeners.addIfAbsent(listener); + } + + public void removeActionPressListener(Consumer<NotificationEntry> listener) { + mActionPressListeners.remove(listener); + } + /** * Activates a given {@link RemoteInput} * @@ -634,6 +646,9 @@ public class NotificationRemoteInputManager implements Dumpable { if (mRemoteInputListener != null) { mRemoteInputListener.releaseNotificationIfKeptForRemoteInputHistory(entry); } + for (Consumer<NotificationEntry> listener : mActionPressListeners) { + listener.accept(entry); + } } /** Returns whether the notification should be lifetime extended for smart reply history */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index df412ed93f55..8cb18a06057e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -260,7 +260,7 @@ public class InstantAppNotifier extends CoreStartable Intent browserIntent = getTaskIntent(taskId, userId); Notification.Builder builder = - new Notification.Builder(mContext, NotificationChannels.GENERAL); + new Notification.Builder(mContext, NotificationChannels.INSTANT); if (browserIntent != null && browserIntent.isWebIntent()) { // Make sure that this doesn't resolve back to an instant app browserIntent diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java index 6be8a491eead..e98ae8db4122 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListDumper.java @@ -121,6 +121,12 @@ public class ListDumper { sb.append(" (parent=") .append(entry.getParent() != null ? entry.getParent().getKey() : null) .append(")"); + + NotificationEntry notifEntry = entry.getRepresentativeEntry(); + if (notifEntry != null) { + sb.append(" rank=") + .append(notifEntry.getRanking().getRank()); + } } if (entry.getSection() != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java index 6085096ee124..410593ac5493 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifCollection.java @@ -609,12 +609,21 @@ public class NotifCollection implements Dumpable { } checkForReentrantCall(); + NotificationEntry collectionEntry = getEntry(entry.getKey()); + String logKey = logKey(entry); + String collectionEntryIs = collectionEntry == null ? "null" + : entry == collectionEntry ? "same" : "different"; + + if (entry != collectionEntry) { + // TODO: We should probably make this throw, but that's too risky right now + mLogger.logEntryBeingExtendedNotInCollection(entry, extender, collectionEntryIs); + } + if (!entry.mLifetimeExtenders.remove(extender)) { throw mEulogizer.record(new IllegalStateException( - String.format( - "Cannot end lifetime extension for extender \"%s\" (%s)", - extender.getName(), - extender))); + String.format("Cannot end lifetime extension for extender \"%s\"" + + " of entry %s (collection entry is %s)", + extender.getName(), logKey, collectionEntryIs))); } mLogger.logLifetimeExtensionEnded( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt index da0169bd6dc4..a61db4107c94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt @@ -18,7 +18,6 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.app.Notification import android.app.Notification.GROUP_ALERT_SUMMARY import android.util.ArrayMap -import android.util.ArraySet import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.NotificationRemoteInputManager import com.android.systemui.statusbar.notification.collection.GroupEntry @@ -41,6 +40,7 @@ import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.time.SystemClock +import java.util.function.Consumer import javax.inject.Inject /** @@ -85,6 +85,7 @@ class HeadsUpCoordinator @Inject constructor( pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilter) pipeline.addPromoter(mNotifPromoter) pipeline.addNotificationLifetimeExtender(mLifetimeExtender) + mRemoteInputManager.addActionPressListener(mActionPressListener) } private fun onHeadsUpViewBound(entry: NotificationEntry) { @@ -448,6 +449,14 @@ class HeadsUpCoordinator @Inject constructor( (entry.sbn.notification.flags and Notification.FLAG_ONLY_ALERT_ONCE) == 0) } + /** When an action is pressed on a notification, end HeadsUp lifetime extension. */ + private val mActionPressListener = Consumer<NotificationEntry> { entry -> + if (mNotifsExtendingLifetime.contains(entry)) { + val removeInMillis = mHeadsUpManager.getEarliestRemovalTime(entry.key) + mExecutor.executeDelayed({ endNotifLifetimeExtensionIfExtended(entry) }, removeInMillis) + } + } + private val mLifetimeExtender = object : NotifLifetimeExtender { override fun getName() = TAG @@ -503,6 +512,7 @@ class HeadsUpCoordinator @Inject constructor( private val mOnHeadsUpChangedListener = object : OnHeadsUpChangedListener { override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) { if (!isHeadsUp) { + mNotifPromoter.invalidateList() mHeadsUpViewBinder.unbindHeadsUpView(entry) endNotifLifetimeExtensionIfExtended(entry) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java index d7bd95c0949e..b9d23b4a344a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinator.java @@ -16,8 +16,9 @@ package com.android.systemui.statusbar.notification.collection.coordinator; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE; -import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING; +import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP; + +import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -35,6 +36,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider; import com.android.systemui.statusbar.phone.NotifPanelEvents; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.util.Compile; import com.android.systemui.util.concurrency.DelayableExecutor; import java.io.PrintWriter; @@ -54,6 +56,8 @@ import javax.inject.Inject; @SysUISingleton public class VisualStabilityCoordinator implements Coordinator, Dumpable, NotifPanelEvents.Listener { + public static final String TAG = "VisualStability"; + public static final boolean DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE); private final DelayableExecutor mDelayableExecutor; private final HeadsUpManager mHeadsUpManager; private final NotifPanelEvents mNotifPanelEvents; @@ -61,7 +65,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, private final VisualStabilityProvider mVisualStabilityProvider; private final WakefulnessLifecycle mWakefulnessLifecycle; - private boolean mScreenOn; + private boolean mSleepy = true; + private boolean mFullyDozed; private boolean mPanelExpanded; private boolean mPulsing; private boolean mNotifPanelCollapsing; @@ -104,8 +109,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, @Override public void attach(NotifPipeline pipeline) { mWakefulnessLifecycle.addObserver(mWakefulnessObserver); - mScreenOn = mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE - || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING; + mSleepy = mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP; + mFullyDozed = mStatusBarStateController.getDozeAmount() == 1f; mStatusBarStateController.addCallback(mStatusBarStateControllerListener); mPulsing = mStatusBarStateController.isPulsing(); @@ -113,6 +118,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, pipeline.setVisualStabilityManager(mNotifStabilityManager); } + // TODO(b/203826051): Ensure stability manager can allow reordering off-screen // HUNs to the top of the shade private final NotifStabilityManager mNotifStabilityManager = @@ -174,9 +180,18 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, } }; - private void updateAllowedStates() { + private void updateAllowedStates(String field, boolean value) { + boolean wasPipelineRunAllowed = mPipelineRunAllowed; + boolean wasReorderingAllowed = mReorderingAllowed; mPipelineRunAllowed = !isPanelCollapsingOrLaunchingActivity(); mReorderingAllowed = isReorderingAllowed(); + if (DEBUG && (wasPipelineRunAllowed != mPipelineRunAllowed + || wasReorderingAllowed != mReorderingAllowed)) { + Log.d(TAG, "Stability allowances changed:" + + " pipelineRunAllowed " + wasPipelineRunAllowed + "->" + mPipelineRunAllowed + + " reorderingAllowed " + wasReorderingAllowed + "->" + mReorderingAllowed + + " when setting " + field + "=" + value); + } if ((mPipelineRunAllowed && mIsSuppressingPipelineRun) || (mReorderingAllowed && (mIsSuppressingGroupChange || isSuppressingSectionChange() @@ -195,7 +210,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, } private boolean isReorderingAllowed() { - return (!mScreenOn || !mPanelExpanded) && !mPulsing; + return ((mFullyDozed && mSleepy) || !mPanelExpanded) && !mPulsing; } /** @@ -235,27 +250,37 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, @Override public void onPulsingChanged(boolean pulsing) { mPulsing = pulsing; - updateAllowedStates(); + updateAllowedStates("pulsing", pulsing); } @Override public void onExpandedChanged(boolean expanded) { mPanelExpanded = expanded; - updateAllowedStates(); + updateAllowedStates("panelExpanded", expanded); + } + + @Override + public void onDozeAmountChanged(float linear, float eased) { + final boolean fullyDozed = linear == 1f; + mFullyDozed = fullyDozed; + updateAllowedStates("fullyDozed", fullyDozed); } }; final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() { @Override public void onFinishedGoingToSleep() { - mScreenOn = false; - updateAllowedStates(); + // NOTE: this method is called much earlier than what we consider "finished" going to + // sleep (the animation isn't done), so we also need to check the doze amount is not 1 + // and use the combo to determine that the locked shade is not visible. + mSleepy = true; + updateAllowedStates("sleepy", true); } @Override public void onStartedWakingUp() { - mScreenOn = true; - updateAllowedStates(); + mSleepy = false; + updateAllowedStates("sleepy", false); } }; @@ -265,7 +290,8 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, pw.println(" notifPanelCollapsing: " + mNotifPanelCollapsing); pw.println(" launchingNotifActivity: " + mNotifPanelLaunchingActivity); pw.println("reorderingAllowed: " + mReorderingAllowed); - pw.println(" screenOn: " + mScreenOn); + pw.println(" sleepy: " + mSleepy); + pw.println(" fullyDozed: " + mFullyDozed); pw.println(" panelExpanded: " + mPanelExpanded); pw.println(" pulsing: " + mPulsing); pw.println("isSuppressingPipelineRun: " + mIsSuppressingPipelineRun); @@ -285,12 +311,12 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, @Override public void onPanelCollapsingChanged(boolean isCollapsing) { mNotifPanelCollapsing = isCollapsing; - updateAllowedStates(); + updateAllowedStates("notifPanelCollapsing", isCollapsing); } @Override public void onLaunchingActivityChanged(boolean isLaunchingActivity) { mNotifPanelLaunchingActivity = isLaunchingActivity; - updateAllowedStates(); + updateAllowedStates("notifPanelLaunchingActivity", isLaunchingActivity); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt index 263737e20a13..ea66f3b6dd42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt @@ -25,12 +25,9 @@ data class NotifSection( val sectioner: NotifSectioner, val index: Int ) { - val label: String - get() = "Section($index, $bucket, \"${sectioner.name}\")" - + @PriorityBucket + val bucket: Int = sectioner.bucket + val label: String = "$index:$bucket:${sectioner.name}" val headerController: NodeController? = sectioner.headerNodeController - val comparator: NotifComparator? = sectioner.comparator - - @PriorityBucket val bucket: Int = sectioner.bucket } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt index 7e7936717b84..ac0b1ee6c442 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt @@ -248,6 +248,20 @@ class NotifCollectionLogger @Inject constructor( }) } + fun logEntryBeingExtendedNotInCollection( + entry: NotificationEntry, + extender: NotifLifetimeExtender, + collectionEntryIs: String + ) { + buffer.log(TAG, WARNING, { + str1 = entry.logKey + str2 = extender.name + str3 = collectionEntryIs + }, { + "While ending lifetime extension by $str2 of $str1, entry in collection is $str3" + }) + } + fun logFutureDismissalReused(dismissal: FutureDismissal) { buffer.log(TAG, INFO, { str1 = dismissal.label diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index e210f193b0a1..a063dbd99626 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -194,6 +194,10 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return false; } + if (!canAlertHeadsUpCommon(entry)) { + return false; + } + if (!canAlertAwakeCommon(entry)) { return false; } @@ -267,6 +271,11 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return false; } + if (!canAlertHeadsUpCommon(entry)) { + mLogger.logNoPulsingNoAlert(sbn); + return false; + } + if (entry.shouldSuppressAmbient()) { mLogger.logNoPulsingNoAmbientEffect(sbn); return false; @@ -294,12 +303,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return false; } - // Don't alert notifications that are suppressed due to group alert behavior - if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) { - mLogger.logNoAlertingGroupAlertBehavior(sbn); - return false; - } - for (int i = 0; i < mSuppressors.size(); i++) { if (mSuppressors.get(i).suppressInterruptions(entry)) { mLogger.logNoAlertingSuppressedBy(sbn, mSuppressors.get(i), /* awake */ false); @@ -307,13 +310,31 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter } } - if (entry.hasJustLaunchedFullScreenIntent()) { - mLogger.logNoAlertingRecentFullscreen(sbn); + if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) { + mLogger.keyguardHideNotification(entry.getKey()); return false; } - if (mKeyguardNotificationVisibilityProvider.shouldHideNotification(entry)) { - mLogger.keyguardHideNotification(entry.getKey()); + return true; + } + + /** + * Common checks for heads up notifications on regular and AOD displays. + * + * @param entry the entry to check + * @return true if these checks pass, false if the notification should not alert + */ + private boolean canAlertHeadsUpCommon(NotificationEntry entry) { + StatusBarNotification sbn = entry.getSbn(); + + // Don't alert notifications that are suppressed due to group alert behavior + if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) { + mLogger.logNoAlertingGroupAlertBehavior(sbn); + return false; + } + + if (entry.hasJustLaunchedFullScreenIntent()) { + mLogger.logNoAlertingRecentFullscreen(sbn); return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 1cae60b3d37d..2bef4de295b3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -2075,6 +2075,13 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public void applyLaunchAnimationParams(LaunchAnimationParameters params) { if (params == null) { + // `null` params indicates the animation is over, which means we can't access + // params.getParentStartClipTopAmount() which has the value we want to restore. + // Fortunately, only NotificationShelf actually uses these values for anything other + // than this launch animation, so we can restore the value to 0 and it's right for now. + if (mNotificationParent != null) { + mNotificationParent.setClipTopAmount(0); + } return; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index d71e76801b3b..e042e9cb2a19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -58,7 +58,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable { private ArrayList<View> mMatchParentViews = new ArrayList<View>(); private static Rect mClipRect = new Rect(); private boolean mWillBeGone; - private int mMinClipTopAmount = 0; private boolean mClipToActualHeight = true; private boolean mChangingPosition = false; private ViewGroup mTransientContainer; @@ -477,14 +476,6 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable { mWillBeGone = willBeGone; } - public int getMinClipTopAmount() { - return mMinClipTopAmount; - } - - public void setMinClipTopAmount(int minClipTopAmount) { - mMinClipTopAmount = minClipTopAmount; - } - @Override public void setLayerType(int layerType, Paint paint) { // Allow resetting the layerType to NONE regardless of overlappingRendering diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java index 56f8e087d64d..40a44ffd7fe3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridGroupManager.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.row; +import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; + import android.annotation.Nullable; import android.app.Notification; import android.content.Context; @@ -136,8 +138,8 @@ public class HybridGroupManager { if (!text.equals(reusableView.getText())) { reusableView.setText(text); } - String contentDescription = String.format(mContext.getResources().getQuantityString( - R.plurals.notification_group_overflow_description, number), number); + String contentDescription = icuMessageFormat(mContext.getResources(), + R.string.notification_group_overflow_description, number); reusableView.setContentDescription(contentDescription); reusableView.setTextSize(TypedValue.COMPLEX_UNIT_PX, mOverflowNumberSize); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java index 7269f5545163..512b04968166 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.row; +import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; @@ -243,11 +245,11 @@ public class NotificationSnooze extends LinearLayout private SnoozeOption createOption(int minutes, int accessibilityActionId) { Resources res = getResources(); boolean showInHours = minutes >= 60; - int pluralResId = showInHours - ? R.plurals.snoozeHourOptions - : R.plurals.snoozeMinuteOptions; + int stringResId = showInHours + ? R.string.snoozeHourOptions + : R.string.snoozeMinuteOptions; int count = showInHours ? (minutes / 60) : minutes; - String description = res.getQuantityString(pluralResId, count, count); + String description = icuMessageFormat(res, stringResId, count); String resultText = String.format(res.getString(R.string.snoozed_for_time), description); AccessibilityAction action = new AccessibilityAction(accessibilityActionId, description); final int index = resultText.indexOf(description); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index a552f999aeb4..a76f0827fc18 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -1312,6 +1312,7 @@ public class NotificationChildrenContainer extends ViewGroup } float bottomRoundness = last ? currentBottomRoundness : 0.0f; child.setBottomRoundness(bottomRoundness, isShown() /* animate */); + child.setTopRoundness(0.0f, false /* animate */); last = false; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index ba57d57d0fd3..368122230fb8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -4531,31 +4531,12 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mClearAllInProgress = clearAllInProgress; mAmbientState.setClearAllInProgress(clearAllInProgress); mController.getNoticationRoundessManager().setClearAllInProgress(clearAllInProgress); - handleClearAllClipping(); } boolean getClearAllInProgress() { return mClearAllInProgress; } - @ShadeViewRefactor(RefactorComponent.ADAPTER) - private void handleClearAllClipping() { - final int count = getChildCount(); - boolean previousChildWillBeDismissed = false; - for (int i = 0; i < count; i++) { - ExpandableView child = (ExpandableView) getChildAt(i); - if (child.getVisibility() == GONE) { - continue; - } - if (mClearAllInProgress && previousChildWillBeDismissed) { - child.setMinClipTopAmount(child.getClipTopAmount()); - } else { - child.setMinClipTopAmount(0); - } - previousChildWillBeDismissed = canChildBeCleared(child); - } - } - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public boolean isFooterViewNotGone() { return mFooterView != null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index fc043b114479..2c22bc68c198 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -448,6 +448,8 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn void setBouncerShowing(boolean bouncerShowing); + void setBouncerShowingOverDream(boolean bouncerShowingOverDream); + void collapseShade(); int getWakefulnessState(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index fdb756de399d..b3e0073de7a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -442,6 +442,7 @@ public class CentralSurfacesImpl extends CoreStartable implements */ protected int mState; // TODO: remove this. Just use StatusBarStateController protected boolean mBouncerShowing; + private boolean mBouncerShowingOverDream; private final PhoneStatusBarPolicy mIconPolicy; @@ -2516,6 +2517,12 @@ public class CentralSurfacesImpl extends CoreStartable implements animate, intent.getPackage(), (adapter) -> { ActivityOptions options = new ActivityOptions( CentralSurfaces.getActivityOptions(mDisplayId, adapter)); + + // We know that the intent of the caller is to dismiss the keyguard and + // this runnable is called right after the keyguard is solved, so we tell + // WM that we should dismiss it to avoid flickers when opening an activity + // that can also be shown over the keyguard. + options.setDismissKeyguard(); options.setDisallowEnterPictureInPictureWhileLaunching( disallowEnterPictureInPictureWhileLaunching); if (CameraIntents.isInsecureCameraIntent(intent)) { @@ -3327,9 +3334,13 @@ public class CentralSurfacesImpl extends CoreStartable implements @Override public boolean onBackPressed() { - boolean isScrimmedBouncer = mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED; - if (mStatusBarKeyguardViewManager.onBackPressed(isScrimmedBouncer /* hideImmediately */)) { - if (isScrimmedBouncer) { + final boolean isScrimmedBouncer = + mScrimController.getState() == ScrimState.BOUNCER_SCRIMMED; + final boolean isBouncerOverDream = isBouncerShowingOverDream(); + + if (mStatusBarKeyguardViewManager.onBackPressed( + isScrimmedBouncer || isBouncerOverDream /* hideImmediately */)) { + if (isScrimmedBouncer || isBouncerOverDream) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(false); } else { mNotificationPanelViewController.expandWithoutQs(); @@ -3351,7 +3362,8 @@ public class CentralSurfacesImpl extends CoreStartable implements if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) { return true; } - if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED) { + if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED + && !isBouncerOverDream) { if (mNotificationPanelViewController.canPanelBeCollapsed()) { mShadeController.animateCollapsePanels(); } @@ -3564,6 +3576,17 @@ public class CentralSurfacesImpl extends CoreStartable implements } /** + * Sets whether the bouncer over dream is showing. Note that the bouncer over dream is handled + * independently of the rest of the notification panel. As a result, setting this state via + * {@link #setBouncerShowing(boolean)} leads to unintended side effects from states modified + * behind the dream. + */ + @Override + public void setBouncerShowingOverDream(boolean bouncerShowingOverDream) { + mBouncerShowingOverDream = bouncerShowingOverDream; + } + + /** * Propagate the bouncer state to status bar components. * * Separate from {@link #setBouncerShowing} because we sometimes re-create the status bar and @@ -4207,7 +4230,7 @@ public class CentralSurfacesImpl extends CoreStartable implements @Override public boolean isBouncerShowingOverDream() { - return isBouncerShowing() && mDreamOverlayStateController.isOverlayActive(); + return mBouncerShowingOverDream; } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java index 414bc84fcd85..fd307df8d304 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -1195,10 +1195,10 @@ public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickL public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) { mHasCard = !response.getWalletCards().isEmpty(); Drawable tileIcon = mQuickAccessWalletController.getWalletClient().getTileIcon(); + if (tileIcon != null) { + mWalletButton.setImageDrawable(tileIcon); + } post(() -> { - if (tileIcon != null) { - mWalletButton.setImageDrawable(tileIcon); - } updateWalletVisibility(); updateAffordanceColors(); }); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 9afdfd651130..b66d21edb888 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -646,6 +646,7 @@ public class NotificationPanelViewController extends PanelViewController { private int mScrimCornerRadius; private int mScreenCornerRadius; private boolean mQSAnimatingHiddenFromCollapsed; + private boolean mUseLargeScreenShadeHeader; private int mQsClipTop; private int mQsClipBottom; @@ -1137,18 +1138,20 @@ public class NotificationPanelViewController extends PanelViewController { final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled; mSplitShadeEnabled = newSplitShadeEnabled; - boolean useLargeScreenShadeHeader = - LargeScreenUtils.shouldUseLargeScreenShadeHeader(mView.getResources()); if (mQs != null) { mQs.setInSplitShade(mSplitShadeEnabled); } + + mUseLargeScreenShadeHeader = + LargeScreenUtils.shouldUseLargeScreenShadeHeader(mView.getResources()); + mLargeScreenShadeHeaderHeight = mResources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height); - mQuickQsHeaderHeight = useLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : + mQuickQsHeaderHeight = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : SystemBarUtils.getQuickQsOffsetHeight(mView.getContext()); - int topMargin = useLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : + int topMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top); - mLargeScreenShadeHeaderController.setActive(useLargeScreenShadeHeader); + mLargeScreenShadeHeaderController.setActive(mUseLargeScreenShadeHeader); mAmbientState.setStackTopMargin(topMargin); mNotificationsQSContainerController.updateResources(); @@ -2436,8 +2439,8 @@ public class NotificationPanelViewController extends PanelViewController { } /** - * Updates scrim bounds, QS clipping, and KSV clipping as well based on the bounds of the shade - * and QS state. + * Updates scrim bounds, QS clipping, notifications clipping and keyguard status view clipping + * as well based on the bounds of the shade and QS state. */ private void setQSClippingBounds() { final int qsPanelBottomY = calculateQsBottomPosition(computeQsExpansionFraction()); @@ -2513,6 +2516,13 @@ public class NotificationPanelViewController extends PanelViewController { } } + /** + * Applies clipping to quick settings, notifications layout and + * updates bounds of the notifications background (notifications scrim). + * + * The parameters are bounds of the notifications area rectangle, this function + * calculates bounds for the QS clipping based on the notifications bounds. + */ private void applyQSClippingBounds(int left, int top, int right, int bottom, boolean qsVisible) { if (!mAnimateNextNotificationBounds || mKeyguardStatusAreaClipBounds.isEmpty()) { @@ -2648,12 +2658,10 @@ public class NotificationPanelViewController extends PanelViewController { if (mTransitioningToFullShadeProgress > 0.0f) { return mTransitionToFullShadeQSPosition; } else { - int qsBottomY = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight(); - if (qsExpansionFraction != 0.0) { - qsBottomY = (int) MathUtils.lerp( - qsBottomY, mQs.getDesiredHeight(), qsExpansionFraction); - } - return qsBottomY; + int qsBottomYFrom = (int) getHeaderTranslation() + mQs.getQsMinExpansionHeight(); + int expandedTopMargin = mUseLargeScreenShadeHeader ? mLargeScreenShadeHeaderHeight : 0; + int qsBottomYTo = mQs.getDesiredHeight() + expandedTopMargin; + return (int) MathUtils.lerp(qsBottomYFrom, qsBottomYTo, qsExpansionFraction); } } @@ -5012,7 +5020,8 @@ public class NotificationPanelViewController extends PanelViewController { mDebugPaint.setColor(color); canvas.drawLine(/* startX= */ 0, /* startY= */ y, /* stopX= */ mView.getWidth(), /* stopY= */ y, mDebugPaint); - canvas.drawText(label, /* x= */ 0, /* y= */ computeDebugYTextPosition(y), mDebugPaint); + canvas.drawText(label + " = " + y + "px", /* x= */ 0, + /* y= */ computeDebugYTextPosition(y), mDebugPaint); } private int computeDebugYTextPosition(int lineY) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java index faae4bbbafd0..a8da554d7e1f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -502,7 +502,8 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW cb.onStateChanged(mCurrentState.mKeyguardShowing, mCurrentState.mKeyguardOccluded, mCurrentState.mBouncerShowing, - mCurrentState.mDozing); + mCurrentState.mDozing, + mCurrentState.mPanelExpanded); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 82ca842d48c9..fc0c4d6d6f56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -430,6 +430,8 @@ public abstract class PanelViewController { // situations, such keeping your finger down while swiping to unlock to an app // that is locked in landscape (the rotation will cancel the touch event). expand = false; + } else if (mCentralSurfaces.isBouncerShowingOverDream()) { + expand = false; } else { // If we get a cancel, put the shade back to the state it was in when the // gesture started diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 11e9ae90aef6..e3c070e3686e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -163,6 +163,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void onVisibilityChanged(boolean isVisible) { + mCentralSurfaces + .setBouncerShowingOverDream( + isVisible && mDreamOverlayStateController.isOverlayActive()); + if (!isVisible) { mCentralSurfaces.setBouncerHiddenFraction(KeyguardBouncer.EXPANSION_HIDDEN); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java index ac43b679da0f..ae48c2d3b6f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowCallback.java @@ -17,5 +17,5 @@ package com.android.systemui.statusbar.phone; public interface StatusBarWindowCallback { void onStateChanged(boolean keyguardShowing, boolean keyguardOccluded, boolean bouncerShowing, - boolean isDozing); + boolean isDozing, boolean panelExpanded); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionChangeEvent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionChangeEvent.kt index c0384b444511..7c61b299cff9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionChangeEvent.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionChangeEvent.kt @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2019 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.statusbar.phone.panelstate import android.annotation.FloatRange diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 1b685d0aad7a..f151d291cda3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -45,6 +45,7 @@ import android.os.UserManager; import android.provider.Settings; import android.telephony.TelephonyCallback; import android.text.TextUtils; +import android.util.FeatureFlagUtils; import android.util.Log; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -63,6 +64,7 @@ import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.users.UserCreatingDialog; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dumpable; +import com.android.systemui.GuestResetOrExitSessionReceiver; import com.android.systemui.GuestResumeSessionReceiver; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; @@ -121,6 +123,8 @@ public class UserSwitcherController implements Dumpable { private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>(); @VisibleForTesting final GuestResumeSessionReceiver mGuestResumeSessionReceiver; + @VisibleForTesting + final GuestResetOrExitSessionReceiver mGuestResetOrExitSessionReceiver; private final KeyguardStateController mKeyguardStateController; private final DeviceProvisionedController mDeviceProvisionedController; private final DevicePolicyManager mDevicePolicyManager; @@ -188,7 +192,9 @@ public class UserSwitcherController implements Dumpable { InteractionJankMonitor interactionJankMonitor, LatencyTracker latencyTracker, DumpManager dumpManager, - DialogLaunchAnimator dialogLaunchAnimator) { + DialogLaunchAnimator dialogLaunchAnimator, + GuestResumeSessionReceiver guestResumeSessionReceiver, + GuestResetOrExitSessionReceiver guestResetOrExitSessionReceiver) { mContext = context; mActivityManager = activityManager; mUserTracker = userTracker; @@ -200,14 +206,13 @@ public class UserSwitcherController implements Dumpable { mInteractionJankMonitor = interactionJankMonitor; mLatencyTracker = latencyTracker; mGlobalSettings = globalSettings; - mGuestResumeSessionReceiver = new GuestResumeSessionReceiver( - this, mUserTracker, mUiEventLogger, secureSettings); + mGuestResumeSessionReceiver = guestResumeSessionReceiver; + mGuestResetOrExitSessionReceiver = guestResetOrExitSessionReceiver; mBgExecutor = bgExecutor; mLongRunningExecutor = longRunningExecutor; mUiExecutor = uiExecutor; - if (!UserManager.isGuestUserEphemeral()) { - mGuestResumeSessionReceiver.register(mBroadcastDispatcher); - } + mGuestResumeSessionReceiver.register(); + mGuestResetOrExitSessionReceiver.register(); mGuestUserAutoCreated = mContext.getResources().getBoolean( com.android.internal.R.bool.config_guestUserAutoCreated); mGuestIsResetting = new AtomicBoolean(); @@ -278,6 +283,10 @@ public class UserSwitcherController implements Dumpable { refreshUsers(UserHandle.USER_NULL); } + private static boolean isEnableGuestModeUxChanges(Context context) { + return FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES); + } + /** * Refreshes users from UserManager. * @@ -524,20 +533,31 @@ public class UserSwitcherController implements Dumpable { private void onUserListItemClicked(int id, UserRecord record, DialogShower dialogShower) { int currUserId = mUserTracker.getUserId(); + // If switching from guest and guest is ephemeral, then follow the flow + // of showExitGuestDialog to remove current guest, + // and switch to selected user + UserInfo currUserInfo = mUserTracker.getUserInfo(); if (currUserId == id) { if (record.isGuest) { - showExitGuestDialog(id, dialogShower); + showExitGuestDialog(id, currUserInfo.isEphemeral(), dialogShower); } return; } - if (UserManager.isGuestUserEphemeral()) { - // If switching from guest, we want to bring up the guest exit dialog instead of switching - UserInfo currUserInfo = mUserManager.getUserInfo(currUserId); - if (currUserInfo != null && currUserInfo.isGuest()) { - showExitGuestDialog(currUserId, record.resolveId(), dialogShower); + + if (currUserInfo != null && currUserInfo.isGuest()) { + if (isEnableGuestModeUxChanges(mContext)) { + showExitGuestDialog(currUserId, currUserInfo.isEphemeral(), + record.resolveId(), dialogShower); return; + } else { + if (currUserInfo.isEphemeral()) { + showExitGuestDialog(currUserId, currUserInfo.isEphemeral(), + record.resolveId(), dialogShower); + return; + } } } + if (dialogShower != null) { // If we haven't morphed into another dialog, it means we have just switched users. // Then, dismiss the dialog. @@ -559,7 +579,7 @@ public class UserSwitcherController implements Dumpable { } } - private void showExitGuestDialog(int id, DialogShower dialogShower) { + private void showExitGuestDialog(int id, boolean isGuestEphemeral, DialogShower dialogShower) { int newId = UserHandle.USER_SYSTEM; if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) { UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); @@ -567,14 +587,15 @@ public class UserSwitcherController implements Dumpable { newId = info.id; } } - showExitGuestDialog(id, newId, dialogShower); + showExitGuestDialog(id, isGuestEphemeral, newId, dialogShower); } - private void showExitGuestDialog(int id, int targetId, DialogShower dialogShower) { + private void showExitGuestDialog(int id, boolean isGuestEphemeral, + int targetId, DialogShower dialogShower) { if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) { mExitGuestDialog.cancel(); } - mExitGuestDialog = new ExitGuestDialog(mContext, id, targetId); + mExitGuestDialog = new ExitGuestDialog(mContext, id, isGuestEphemeral, targetId); if (dialogShower != null) { dialogShower.showDialog(mExitGuestDialog); } else { @@ -808,6 +829,52 @@ public class UserSwitcherController implements Dumpable { } } + /** + * Exits guest user and switches to previous non-guest user. The guest must be the current + * user. + * + * @param guestUserId user id of the guest user to exit + * @param targetUserId user id of the guest user to exit, set to UserHandle.USER_NULL when + * target user id is not known + * @param forceRemoveGuestOnExit true: remove guest before switching user, + * false: remove guest only if its ephemeral, else keep guest + */ + public void exitGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId, + boolean forceRemoveGuestOnExit) { + UserInfo currentUser = mUserTracker.getUserInfo(); + if (currentUser.id != guestUserId) { + Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")" + + " is not current user (" + currentUser.id + ")"); + return; + } + if (!currentUser.isGuest()) { + Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")" + + " is not a guest"); + return; + } + + int newUserId = UserHandle.USER_SYSTEM; + if (targetUserId == UserHandle.USER_NULL) { + // when target user is not specified switch to last non guest user + if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) { + UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); + if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) { + newUserId = info.id; + } + } + } else { + newUserId = targetUserId; + } + + if (currentUser.isEphemeral() || forceRemoveGuestOnExit) { + mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); + removeGuestUser(currentUser.id, newUserId); + } else { + mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_SWITCH); + switchToUserId(newUserId); + } + } + private void scheduleGuestCreation() { if (!mGuestCreationScheduled.compareAndSet(false, true)) { return; @@ -975,9 +1042,14 @@ public class UserSwitcherController implements Dumpable { public String getName(Context context, UserRecord item) { if (item.isGuest) { if (item.isCurrent) { - return context.getString(mController.mGuestUserAutoCreated + if (isEnableGuestModeUxChanges(context)) { + return context.getString( + com.android.settingslib.R.string.guest_exit_quick_settings_button); + } else { + return context.getString(mController.mGuestUserAutoCreated ? com.android.settingslib.R.string.guest_reset_guest : com.android.settingslib.R.string.guest_exit_guest); + } } else { if (item.info != null) { return context.getString(com.android.internal.R.string.guest_name); @@ -994,8 +1066,13 @@ public class UserSwitcherController implements Dumpable { ? com.android.settingslib.R.string.guest_resetting : com.android.internal.R.string.guest_name); } else { - return context.getString( - com.android.settingslib.R.string.guest_new_guest); + if (isEnableGuestModeUxChanges(context)) { + // we always show "guest" as string, instead of "add guest" + return context.getString(com.android.internal.R.string.guest_name); + } else { + return context.getString( + com.android.settingslib.R.string.guest_new_guest); + } } } } @@ -1017,7 +1094,11 @@ public class UserSwitcherController implements Dumpable { protected static Drawable getIconDrawable(Context context, UserRecord item) { int iconRes; if (item.isAddUser) { - iconRes = R.drawable.ic_account_circle_filled; + if (isEnableGuestModeUxChanges(context)) { + iconRes = R.drawable.ic_add; + } else { + iconRes = R.drawable.ic_account_circle_filled; + } } else if (item.isGuest) { iconRes = R.drawable.ic_account_circle; } else if (item.isAddSupervisedUser) { @@ -1189,24 +1270,58 @@ public class UserSwitcherController implements Dumpable { private final int mGuestId; private final int mTargetId; + private final boolean mIsGuestEphemeral; - public ExitGuestDialog(Context context, int guestId, int targetId) { + ExitGuestDialog(Context context, int guestId, boolean isGuestEphemeral, + int targetId) { super(context); - setTitle(mGuestUserAutoCreated - ? com.android.settingslib.R.string.guest_reset_guest_dialog_title - : com.android.settingslib.R.string.guest_remove_guest_dialog_title); - setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); - setButton(DialogInterface.BUTTON_NEUTRAL, - context.getString(android.R.string.cancel), this); - setButton(DialogInterface.BUTTON_POSITIVE, - context.getString(mGuestUserAutoCreated + if (isEnableGuestModeUxChanges(context)) { + if (isGuestEphemeral) { + setTitle(context.getString( + com.android.settingslib.R.string.guest_exit_dialog_title)); + setMessage(context.getString( + com.android.settingslib.R.string.guest_exit_dialog_message)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_dialog_button), this); + } else { + setTitle(context.getString( + com.android.settingslib + .R.string.guest_exit_dialog_title_non_ephemeral)); + setMessage(context.getString( + com.android.settingslib + .R.string.guest_exit_dialog_message_non_ephemeral)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_NEGATIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_clear_data_button), + this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString( + com.android.settingslib.R.string.guest_exit_save_data_button), + this); + } + } else { + setTitle(mGuestUserAutoCreated + ? com.android.settingslib.R.string.guest_reset_guest_dialog_title + : com.android.settingslib.R.string.guest_remove_guest_dialog_title); + setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); + setButton(DialogInterface.BUTTON_NEUTRAL, + context.getString(android.R.string.cancel), this); + setButton(DialogInterface.BUTTON_POSITIVE, + context.getString(mGuestUserAutoCreated ? com.android.settingslib.R.string.guest_reset_guest_confirm_button : com.android.settingslib.R.string.guest_remove_guest_confirm_button), - this); + this); + } SystemUIDialog.setWindowOnTop(this, mKeyguardStateController.isShowing()); setCanceledOnTouchOutside(false); mGuestId = guestId; mTargetId = targetId; + mIsGuestEphemeral = isGuestEphemeral; } @Override @@ -1216,12 +1331,40 @@ public class UserSwitcherController implements Dumpable { if (mFalsingManager.isFalseTap(penalty)) { return; } - if (which == BUTTON_NEUTRAL) { - cancel(); + if (isEnableGuestModeUxChanges(getContext())) { + if (mIsGuestEphemeral) { + if (which == DialogInterface.BUTTON_POSITIVE) { + mDialogLaunchAnimator.dismissStack(this); + // Ephemeral guest: exit guest, guest is removed by the system + // on exit, since its marked ephemeral + exitGuestUser(mGuestId, mTargetId, false); + } else if (which == DialogInterface.BUTTON_NEGATIVE) { + // Cancel clicked, do nothing + cancel(); + } + } else { + if (which == DialogInterface.BUTTON_POSITIVE) { + mDialogLaunchAnimator.dismissStack(this); + // Non-ephemeral guest: exit guest, guest is not removed by the system + // on exit, since its marked non-ephemeral + exitGuestUser(mGuestId, mTargetId, false); + } else if (which == DialogInterface.BUTTON_NEGATIVE) { + mDialogLaunchAnimator.dismissStack(this); + // Non-ephemeral guest: remove guest and then exit + exitGuestUser(mGuestId, mTargetId, true); + } else if (which == DialogInterface.BUTTON_NEUTRAL) { + // Cancel clicked, do nothing + cancel(); + } + } } else { - mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); - mDialogLaunchAnimator.dismissStack(this); - removeGuestUser(mGuestId, mTargetId); + if (which == BUTTON_NEUTRAL) { + cancel(); + } else { + mUiEventLogger.log(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE); + mDialogLaunchAnimator.dismissStack(this); + removeGuestUser(mGuestId, mTargetId); + } } } } @@ -1230,10 +1373,17 @@ public class UserSwitcherController implements Dumpable { final class AddUserDialog extends SystemUIDialog implements DialogInterface.OnClickListener { - public AddUserDialog(Context context) { + AddUserDialog(Context context) { super(context); + setTitle(com.android.settingslib.R.string.user_add_user_title); - setMessage(com.android.settingslib.R.string.user_add_user_message_short); + String message = context.getString( + com.android.settingslib.R.string.user_add_user_message_short); + UserInfo currentUser = mUserTracker.getUserInfo(); + if (currentUser != null && currentUser.isGuest() && currentUser.isEphemeral()) { + message += context.getString(R.string.user_add_user_message_guest_remove); + } + setMessage(message); setButton(DialogInterface.BUTTON_NEUTRAL, context.getString(android.R.string.cancel), this); setButton(DialogInterface.BUTTON_POSITIVE, diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt index d2d2361d613d..eea6ac0e72e9 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt @@ -20,6 +20,7 @@ import android.content.Context import android.view.IWindowManager import com.android.systemui.keyguard.LifecycleScreenStatusProvider import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.system.SystemUnfoldSharedModule import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider @@ -34,7 +35,7 @@ import java.util.Optional import javax.inject.Named import javax.inject.Singleton -@Module(includes = [UnfoldSharedModule::class]) +@Module(includes = [UnfoldSharedModule::class, SystemUnfoldSharedModule::class]) class UnfoldTransitionModule { @Provides @UnfoldTransitionATracePrefix fun tracingTagPrefix() = "systemui" @@ -62,11 +63,6 @@ class UnfoldTransitionModule { @Provides @Singleton - fun provideUnfoldTransitionConfig(context: Context): UnfoldTransitionConfig = - createConfig(context) - - @Provides - @Singleton fun provideNaturalRotationProgressProvider( context: Context, windowManager: IWindowManager, diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java index 76dfcb182e97..53da213eb38e 100644 --- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java +++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java @@ -35,11 +35,15 @@ import javax.inject.Inject; public class NotificationChannels extends CoreStartable { public static String ALERTS = "ALR"; public static String SCREENSHOTS_HEADSUP = "SCN_HEADSUP"; - public static String GENERAL = "GEN"; + // Deprecated. Please use or create a more specific channel that users will better understand + @Deprecated + static String GENERAL = "GEN"; public static String STORAGE = "DSK"; public static String BATTERY = "BAT"; public static String TVPIP = TvPipNotificationController.NOTIFICATION_CHANNEL; // "TVPIP" public static String HINTS = "HNT"; + public static String INSTANT = "INS"; + public static String SETUP = "STP"; @Inject public NotificationChannels(Context context) { @@ -64,11 +68,17 @@ public class NotificationChannels extends CoreStartable { context.getString(R.string.notification_channel_alerts), NotificationManager.IMPORTANCE_HIGH); - final NotificationChannel general = new NotificationChannel( - GENERAL, - context.getString(R.string.notification_channel_general), + final NotificationChannel instant = new NotificationChannel( + INSTANT, + context.getString(R.string.notification_channel_instant), NotificationManager.IMPORTANCE_MIN); + final NotificationChannel setup = new NotificationChannel( + SETUP, + context.getString(R.string.notification_channel_setup), + NotificationManager.IMPORTANCE_DEFAULT); + setup.setSound(null, null); + final NotificationChannel storage = new NotificationChannel( STORAGE, context.getString(R.string.notification_channel_storage), @@ -84,7 +94,8 @@ public class NotificationChannels extends CoreStartable { nm.createNotificationChannels(Arrays.asList( alerts, - general, + instant, + setup, storage, createScreenshotChannel( context.getString(R.string.notification_channel_screenshot)), @@ -123,6 +134,11 @@ public class NotificationChannels extends CoreStartable { @Override public void start() { createAll(mContext); + cleanUp(); + } + + private void cleanUp() { + mContext.getSystemService(NotificationManager.class).deleteNotificationChannel(GENERAL); } private static boolean isTv(Context context) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreenListener.aidl b/packages/SystemUI/src/com/android/systemui/util/PluralMessageFormater.kt index 46e4299f99fa..83b471dff4b1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/ISplitScreenListener.aidl +++ b/packages/SystemUI/src/com/android/systemui/util/PluralMessageFormater.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 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. @@ -14,20 +14,16 @@ * limitations under the License. */ -package com.android.wm.shell.stagesplit; +package com.android.systemui.util + +import android.annotation.StringRes +import android.content.res.Resources +import android.util.PluralsMessageFormatter /** - * Listener interface that Launcher attaches to SystemUI to get split-screen callbacks. + * Utility method that provides the localized plural string for the given [messageId] + * using the [count] parameter. */ -oneway interface ISplitScreenListener { - - /** - * Called when the stage position changes. - */ - void onStagePositionChanged(int stage, int position); - - /** - * Called when a task changes stages. - */ - void onTaskStageChanged(int taskId, int stage, boolean visible); -}
\ No newline at end of file +fun icuMessageFormat(res: Resources, @StringRes messageId: Int, count: Int): String { + return PluralsMessageFormatter.format(res, mapOf<String, Any>("count" to count), messageId) +} diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java index d3c6e9aa0da9..313d56febb75 100644 --- a/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java +++ b/packages/SystemUI/src/com/android/systemui/util/condition/Monitor.java @@ -18,6 +18,7 @@ package com.android.systemui.util.condition; import android.util.Log; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.statusbar.policy.CallbackController; import org.jetbrains.annotations.NotNull; @@ -60,21 +61,13 @@ public class Monitor implements CallbackController<Monitor.Callback> { }; @Inject - public Monitor(Executor executor, Set<Condition> conditions, Set<Callback> callbacks) { + public Monitor(@Main Executor executor, Set<Condition> conditions) { mConditions = new HashSet<>(); mExecutor = executor; if (conditions != null) { mConditions.addAll(conditions); } - - if (callbacks == null) { - return; - } - - for (Callback callback : callbacks) { - addCallbackLocked(callback); - } } private void updateConditionMetState() { diff --git a/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java index fc67973fe278..8e739d61b4f2 100644 --- a/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java +++ b/packages/SystemUI/src/com/android/systemui/util/condition/dagger/MonitorComponent.java @@ -34,8 +34,7 @@ public interface MonitorComponent { */ @Subcomponent.Factory interface Factory { - MonitorComponent create(@BindsInstance Set<Condition> conditions, - @BindsInstance Set<Monitor.Callback> callbacks); + MonitorComponent create(@BindsInstance Set<Condition> conditions); } /** diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index 83fc28189db5..e90775de591a 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -78,6 +78,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -121,6 +122,7 @@ public class BubblesManager implements Dumpable { private final Bubbles.SysuiProxy mSysuiProxy; // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline private final List<NotifCallback> mCallbacks = new ArrayList<>(); + private final StatusBarWindowCallback mStatusBarWindowCallback; /** * Creates {@link BubblesManager}, returns {@code null} if Optional {@link Bubbles} not present @@ -278,9 +280,15 @@ public class BubblesManager implements Dumpable { }); + // Store callback in a field so it won't get GC'd + mStatusBarWindowCallback = + (keyguardShowing, keyguardOccluded, bouncerShowing, isDozing, panelExpanded) -> + mBubbles.onNotificationPanelExpandedChanged(panelExpanded); + notificationShadeWindowController.registerCallback(mStatusBarWindowCallback); + mSysuiProxy = new Bubbles.SysuiProxy() { @Override - public void isNotificationShadeExpand(Consumer<Boolean> callback) { + public void isNotificationPanelExpand(Consumer<Boolean> callback) { sysuiMainExecutor.execute(() -> { callback.accept(mNotificationShadeWindowController.getPanelExpanded()); }); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java new file mode 100644 index 000000000000..a61cecbabf30 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/BroadcastDialogTest.java @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2022 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.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.mock; + +import androidx.test.filters.SmallTest; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; +import android.widget.Button; +import android.widget.TextView; + +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.media.dialog.MediaOutputDialogFactory; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class BroadcastDialogTest extends SysuiTestCase { + + private static final String SWITCH_APP = "Music"; + private static final String TEST_PACKAGE = "com.google.android.apps.nbu.files"; + private BroadcastDialog mBroadcastDialog; + private View mDialogView; + private TextView mSubTitle; + private Button mChangeOutputButton; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mBroadcastDialog = new BroadcastDialog(mContext, mock(MediaOutputDialogFactory.class), + SWITCH_APP, TEST_PACKAGE, mock(UiEventLogger.class)); + mBroadcastDialog.show(); + mDialogView = mBroadcastDialog.mDialogView; + } + + @After + public void tearDown() { + mBroadcastDialog.dismiss(); + } + + @Test + public void onCreate_withCurrentApp_checkSwitchAppContent() { + mSubTitle = mDialogView.requireViewById(R.id.dialog_subtitle); + + assertThat(mSubTitle.getText()).isEqualTo( + mContext.getString(R.string.bt_le_audio_broadcast_dialog_sub_title, SWITCH_APP)); + } + + @Test + public void onClick_withChangeOutput_dismissBroadcastDialog() { + mChangeOutputButton = mDialogView.requireViewById(R.id.change_output); + mChangeOutputButton.performClick(); + + assertThat(mBroadcastDialog.isShowing()).isFalse(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java index 3340f2fd5749..abe7ae1a0b1c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java @@ -21,7 +21,9 @@ import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_DOCKED; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING; +import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING_BRIGHT; import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE; +import static com.android.systemui.doze.DozeMachine.State.DOZE_SUSPEND_TRIGGERS; import static com.android.systemui.doze.DozeMachine.State.FINISH; import static com.android.systemui.doze.DozeMachine.State.INITIALIZED; import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; @@ -39,6 +41,8 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.app.UiModeManager; +import android.content.res.Configuration; import android.hardware.display.AmbientDisplayConfiguration; import android.testing.AndroidTestingRunner; import android.testing.UiThreadTest; @@ -50,7 +54,6 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.util.wakelock.WakeLockFake; import org.junit.Before; @@ -70,9 +73,12 @@ public class DozeMachineTest extends SysuiTestCase { private WakefulnessLifecycle mWakefulnessLifecycle; @Mock private DozeLog mDozeLog; - @Mock private DockManager mDockManager; + @Mock + private DockManager mDockManager; @Mock private DozeHost mHost; + @Mock + private UiModeManager mUiModeManager; private DozeServiceFake mServiceFake; private WakeLockFake mWakeLockFake; private AmbientDisplayConfiguration mConfigMock; @@ -89,7 +95,7 @@ public class DozeMachineTest extends SysuiTestCase { when(mDockManager.isHidden()).thenReturn(false); mMachine = new DozeMachine(mServiceFake, mConfigMock, mWakeLockFake, - mWakefulnessLifecycle, mock(BatteryController.class), mDozeLog, mDockManager, + mWakefulnessLifecycle, mUiModeManager, mDozeLog, mDockManager, mHost, new DozeMachine.Part[]{mPartMock}); } @@ -462,4 +468,64 @@ public class DozeMachineTest extends SysuiTestCase { assertEquals(Display.STATE_ON, DOZE_REQUEST_PULSE.screenState(dozeParameters)); } + + @Test + public void testTransitionToInitialized_carModeIsEnabled() { + when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR); + mMachine.requestState(INITIALIZED); + + verify(mPartMock).transitionTo(UNINITIALIZED, INITIALIZED); + verify(mPartMock).transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS); + assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState()); + } + + @Test + public void testTransitionToFinish_carModeIsEnabled() { + when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR); + mMachine.requestState(INITIALIZED); + mMachine.requestState(FINISH); + + assertEquals(FINISH, mMachine.getState()); + } + + @Test + public void testDozeToDozeSuspendTriggers_carModeIsEnabled() { + when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR); + mMachine.requestState(INITIALIZED); + mMachine.requestState(DOZE); + + assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState()); + } + + @Test + public void testDozeAoDToDozeSuspendTriggers_carModeIsEnabled() { + when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR); + mMachine.requestState(INITIALIZED); + mMachine.requestState(DOZE_AOD); + + assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState()); + } + + @Test + public void testDozePulsingBrightDozeSuspendTriggers_carModeIsEnabled() { + when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR); + mMachine.requestState(INITIALIZED); + mMachine.requestState(DOZE_PULSING_BRIGHT); + + assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState()); + } + + @Test + public void testDozeAodDockedDozeSuspendTriggers_carModeIsEnabled() { + when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR); + mMachine.requestState(INITIALIZED); + mMachine.requestState(DOZE_AOD_DOCKED); + + assertEquals(DOZE_SUSPEND_TRIGGERS, mMachine.getState()); + } + + @Test + public void testDozeSuppressTriggers_screenState() { + assertEquals(Display.STATE_OFF, DOZE_SUSPEND_TRIGGERS.screenState(null)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index 2e7b88d5fd38..03827dab0c96 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -24,6 +24,7 @@ import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING; import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE; +import static com.android.systemui.doze.DozeMachine.State.DOZE_SUSPEND_TRIGGERS; import static com.android.systemui.doze.DozeMachine.State.FINISH; import static com.android.systemui.doze.DozeMachine.State.INITIALIZED; import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; @@ -182,6 +183,21 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { } @Test + public void dozeSuspendTriggers_doesNotUseLightSensor() { + // GIVEN the device is DOZE and the display state changes to ON + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS); + waitForSensorManager(); + + // WHEN new sensor event sent + mSensor.sendSensorEvent(3); + + // THEN brightness is NOT changed, it's set to the default brightness + assertNotSame(3, mServiceFake.screenBrightness); + assertEquals(DEFAULT_BRIGHTNESS, mServiceFake.screenBrightness); + } + + @Test public void aod_usesLightSensor() { // GIVEN the device is DOZE_AOD and the display state changes to ON mScreen.transitionTo(UNINITIALIZED, INITIALIZED); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java index aa0a909622bb..0f29dcd5a939 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSuppressorTest.java @@ -16,19 +16,26 @@ package com.android.systemui.doze; +import static android.app.UiModeManager.ACTION_ENTER_CAR_MODE; +import static android.app.UiModeManager.ACTION_EXIT_CAR_MODE; + import static com.android.systemui.doze.DozeMachine.State.DOZE; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; +import static com.android.systemui.doze.DozeMachine.State.DOZE_SUSPEND_TRIGGERS; import static com.android.systemui.doze.DozeMachine.State.FINISH; import static com.android.systemui.doze.DozeMachine.State.INITIALIZED; import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; -import static org.mockito.Matchers.anyObject; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.UiModeManager; import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.IntentFilter; import android.content.res.Configuration; import android.hardware.display.AmbientDisplayConfiguration; import android.testing.AndroidTestingRunner; @@ -77,7 +84,10 @@ public class DozeSuppressorTest extends SysuiTestCase { @Captor private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; + @Captor + private ArgumentCaptor<IntentFilter> mIntentFilterCaptor; private BroadcastReceiver mBroadcastReceiver; + private IntentFilter mIntentFilter; @Captor private ArgumentCaptor<DozeHost.Callback> mDozeHostCaptor; @@ -122,15 +132,59 @@ public class DozeSuppressorTest extends SysuiTestCase { } @Test - public void testEndDoze_carMode() { + public void testSuspendTriggersDoze_carMode() { // GIVEN car mode when(mUiModeManager.getCurrentModeType()).thenReturn(Configuration.UI_MODE_TYPE_CAR); // WHEN dozing begins mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED); - // THEN doze immediately ends - verify(mDozeMachine).requestState(FINISH); + // THEN doze continues with all doze triggers disabled. + verify(mDozeMachine).requestState(DOZE_SUSPEND_TRIGGERS); + } + + @Test + public void testSuspendTriggersDoze_enterCarMode() { + // GIVEN currently dozing + mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED); + captureBroadcastReceiver(); + mDozeSuppressor.transitionTo(INITIALIZED, DOZE); + + // WHEN car mode entered + mBroadcastReceiver.onReceive(null, new Intent(ACTION_ENTER_CAR_MODE)); + + // THEN doze continues with all doze triggers disabled. + verify(mDozeMachine).requestState(DOZE_SUSPEND_TRIGGERS); + } + + @Test + public void testDozeResume_exitCarMode() { + // GIVEN currently suspended, with AOD not enabled + when(mConfig.alwaysOnEnabled(anyInt())).thenReturn(false); + mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED); + captureBroadcastReceiver(); + mDozeSuppressor.transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS); + + // WHEN exiting car mode + mBroadcastReceiver.onReceive(null, new Intent(ACTION_EXIT_CAR_MODE)); + + // THEN doze is resumed + verify(mDozeMachine).requestState(DOZE); + } + + @Test + public void testDozeAoDResume_exitCarMode() { + // GIVEN currently suspended, with AOD not enabled + when(mConfig.alwaysOnEnabled(anyInt())).thenReturn(true); + mDozeSuppressor.transitionTo(UNINITIALIZED, INITIALIZED); + captureBroadcastReceiver(); + mDozeSuppressor.transitionTo(INITIALIZED, DOZE_SUSPEND_TRIGGERS); + + // WHEN exiting car mode + mBroadcastReceiver.onReceive(null, new Intent(ACTION_EXIT_CAR_MODE)); + + // THEN doze AOD is resumed + verify(mDozeMachine).requestState(DOZE_AOD); } @Test @@ -225,7 +279,11 @@ public class DozeSuppressorTest extends SysuiTestCase { private void captureBroadcastReceiver() { verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(), - anyObject()); + mIntentFilterCaptor.capture()); mBroadcastReceiver = mBroadcastReceiverCaptor.getValue(); + mIntentFilter = mIntentFilterCaptor.getValue(); + assertEquals(2, mIntentFilter.countActions()); + org.hamcrest.MatcherAssert.assertThat(() -> mIntentFilter.actionsIterator(), + containsInAnyOrder(ACTION_ENTER_CAR_MODE, ACTION_EXIT_CAR_MODE)); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index 4eeb4acf18b8..01a1a3718765 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -17,6 +17,8 @@ package com.android.systemui.doze; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; +import static com.android.systemui.doze.DozeMachine.State.INITIALIZED; +import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; @@ -133,7 +135,7 @@ public class DozeTriggersTest extends SysuiTestCase { ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class); doAnswer(invocation -> null).when(mHost).addCallback(captor.capture()); - mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); + mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED); mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); clearInvocations(mMachine); @@ -192,8 +194,21 @@ public class DozeTriggersTest extends SysuiTestCase { } @Test + public void transitionToDozeSuspendTriggers_disablesAllCallbacks() { + mTriggers.transitionTo(UNINITIALIZED, INITIALIZED); + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_SUSPEND_TRIGGERS); + + mTriggers.transitionTo(DozeMachine.State.INITIALIZED, + DozeMachine.State.DOZE_SUSPEND_TRIGGERS); + + verify(mDockManager).removeListener(any()); + verify(mBroadcastDispatcher).unregisterReceiver(any()); + verify(mHost).removeCallback(any()); + } + + @Test public void testDockEventListener_registerAndUnregister() { - mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); + mTriggers.transitionTo(UNINITIALIZED, DozeMachine.State.INITIALIZED); verify(mDockManager).addListener(any()); mTriggers.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java index b7de6c15459c..25d4c472dc63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayNotificationCountProviderTest.java @@ -16,6 +16,7 @@ package com.android.systemui.dreams; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -48,6 +49,8 @@ public class DreamOverlayNotificationCountProviderTest extends SysuiTestCase { @Mock StatusBarNotification mNotification2; @Mock + StatusBarNotification mNotification3; + @Mock NotificationListenerService.RankingMap mRankingMap; private DreamOverlayNotificationCountProvider mProvider; @@ -58,6 +61,8 @@ public class DreamOverlayNotificationCountProviderTest extends SysuiTestCase { when(mNotification1.getKey()).thenReturn("key1"); when(mNotification2.getKey()).thenReturn("key2"); + when(mNotification3.getKey()).thenReturn("key3"); + when(mNotification3.isOngoing()).thenReturn(true); final StatusBarNotification[] notifications = {mNotification1}; when(mNotificationListener.getActiveNotifications()).thenReturn(notifications); @@ -83,4 +88,13 @@ public class DreamOverlayNotificationCountProviderTest extends SysuiTestCase { handlerArgumentCaptor.getValue().onNotificationRemoved(mNotification1, mRankingMap); verify(mCallback).onNotificationCountChanged(0); } + + @Test + public void testPostingOngoingNotificationDoesNotCallCallbackWithNotificationCount() { + final ArgumentCaptor<NotificationHandler> handlerArgumentCaptor = + ArgumentCaptor.forClass(NotificationHandler.class); + verify(mNotificationListener).addNotificationHandler(handlerArgumentCaptor.capture()); + handlerArgumentCaptor.getValue().onNotificationPosted(mNotification3, mRankingMap); + verify(mCallback, never()).onNotificationCountChanged(2); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java index ed1cf69ad8c0..cefdf283fc7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/SmartSpaceComplicationTest.java @@ -27,7 +27,7 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.dreams.smartspace.DreamsSmartspaceController; +import com.android.systemui.dreams.smartspace.DreamSmartspaceController; import com.android.systemui.plugins.BcSmartspaceDataPlugin; import org.junit.Before; @@ -47,7 +47,7 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { private Context mContext; @Mock - private DreamsSmartspaceController mSmartspaceController; + private DreamSmartspaceController mSmartspaceController; @Mock private DreamOverlayStateController mDreamOverlayStateController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java index 89c82fbff901..c3fca29a9883 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java @@ -422,7 +422,29 @@ public class BouncerSwipeTouchHandlerTest extends SysuiTestCase { verify(mUiEventLogger).log(BouncerSwipeTouchHandler.DreamEvent.DREAM_BOUNCER_FULLY_VISIBLE); } + /** + * Ensures {@link CentralSurfaces} + */ + @Test + public void testInformBouncerShowingOnExpand() { + swipeToPosition(1f, Direction.UP, 0); + } + + /** + * Ensures {@link CentralSurfaces} + */ + @Test + public void testInformBouncerHidingOnCollapse() { + // Must swipe up to set initial state. + swipeToPosition(1f, Direction.UP, 0); + Mockito.clearInvocations(mCentralSurfaces); + + swipeToPosition(0f, Direction.DOWN, 0); + } + + private void swipeToPosition(float percent, Direction direction, float velocityY) { + Mockito.clearInvocations(mTouchSession); mTouchHandler.onSessionStart(mTouchSession); ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerCaptor = ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/HideComplicationTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/HideComplicationTouchHandlerTest.java index 0a021331d9d1..14a5702c8e5b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/HideComplicationTouchHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/HideComplicationTouchHandlerTest.java @@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.dreams.complication.Complication; import com.android.systemui.shared.system.InputChannelCompat; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.touch.TouchInsetManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -59,6 +60,9 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase { TouchInsetManager mTouchInsetManager; @Mock + StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + + @Mock Handler mHandler; @Mock @@ -83,12 +87,45 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase { mVisibilityController, RESTORE_TIMEOUT, mTouchInsetManager, + mStatusBarKeyguardViewManager, mFakeExecutor, mHandler); // Report multiple active sessions. when(mSession.getActiveSessionCount()).thenReturn(2); + // Bouncer hidden. + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + + // Start session. + touchHandler.onSessionStart(mSession); + + // Verify session end. + verify(mSession).pop(); + + // Verify no interaction with visibility controller. + verify(mVisibilityController, never()).setVisibility(anyInt(), anyBoolean()); + } + + /** + * Ensures no actions are taken when the bouncer is showing. + */ + @Test + public void testSessionEndWhenBouncerShowing() { + final HideComplicationTouchHandler touchHandler = new HideComplicationTouchHandler( + mVisibilityController, + RESTORE_TIMEOUT, + mTouchInsetManager, + mStatusBarKeyguardViewManager, + mFakeExecutor, + mHandler); + + // Report one session. + when(mSession.getActiveSessionCount()).thenReturn(1); + + // Bouncer is showing. + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true); + // Start session. touchHandler.onSessionStart(mSession); @@ -108,12 +145,16 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase { mVisibilityController, RESTORE_TIMEOUT, mTouchInsetManager, + mStatusBarKeyguardViewManager, mFakeExecutor, mHandler); // Report one session when(mSession.getActiveSessionCount()).thenReturn(1); + // Bouncer hidden. + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + // Start session touchHandler.onSessionStart(mSession); @@ -149,12 +190,16 @@ public class HideComplicationTouchHandlerTest extends SysuiTestCase { mVisibilityController, RESTORE_TIMEOUT, mTouchInsetManager, + mStatusBarKeyguardViewManager, mFakeExecutor, mHandler); // Report one session when(mSession.getActiveSessionCount()).thenReturn(1); + // Bouncer hidden. + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + // Start session touchHandler.onSessionStart(mSession); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index 241ed2443e6c..540f2a534854 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -58,6 +58,7 @@ import com.android.internal.logging.InstanceId import com.android.systemui.ActivityIntentHelper import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.bluetooth.BroadcastDialogController import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.media.MediaControlPanel.KEY_SMARTSPACE_APP_NAME import com.android.systemui.media.dialog.MediaOutputDialogFactory @@ -104,6 +105,7 @@ private const val SESSION_ARTIST = "SESSION_ARTIST" private const val SESSION_TITLE = "SESSION_TITLE" private const val DISABLED_DEVICE_NAME = "DISABLED_DEVICE_NAME" private const val REC_APP_NAME = "REC APP NAME" +private const val APP_NAME = "APP_NAME" @SmallTest @RunWith(AndroidTestingRunner::class) @@ -130,6 +132,7 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var mediaCarouselController: MediaCarouselController @Mock private lateinit var falsingManager: FalsingManager @Mock private lateinit var transitionParent: ViewGroup + @Mock private lateinit var broadcastDialogController: BroadcastDialogController private lateinit var appIcon: ImageView @Mock private lateinit var albumView: ImageView private lateinit var titleText: TextView @@ -160,8 +163,9 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var dismissText: TextView private lateinit var session: MediaSession - private val device = MediaDeviceData(true, null, DEVICE_NAME) - private val disabledDevice = MediaDeviceData(false, null, DISABLED_DEVICE_NAME) + private lateinit var device: MediaDeviceData + private val disabledDevice = MediaDeviceData(false, null, DISABLED_DEVICE_NAME, null, + showBroadcastButton = false) private lateinit var mediaData: MediaData private val clock = FakeSystemClock() @Mock private lateinit var logger: MediaUiEventLogger @@ -187,6 +191,7 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var recSubtitle1: TextView private lateinit var recSubtitle2: TextView private lateinit var recSubtitle3: TextView + private var shouldShowBroadcastButton: Boolean = false @JvmField @Rule val mockito = MockitoJUnit.rule() @@ -223,7 +228,8 @@ public class MediaControlPanelTest : SysuiTestCase() { logger, keyguardStateController, activityIntentHelper, - lockscreenUserManager) { + lockscreenUserManager, + broadcastDialogController) { override fun loadAnimator( animId: Int, otionInterpolator: Interpolator, @@ -236,28 +242,7 @@ public class MediaControlPanelTest : SysuiTestCase() { initGutsViewHolderMocks() initMediaViewHolderMocks() - // Create media session - val metadataBuilder = MediaMetadata.Builder().apply { - putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST) - putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) - } - val playbackBuilder = PlaybackState.Builder().apply { - setState(PlaybackState.STATE_PAUSED, 6000L, 1f) - setActions(PlaybackState.ACTION_PLAY) - } - session = MediaSession(context, SESSION_KEY).apply { - setMetadata(metadataBuilder.build()) - setPlaybackState(playbackBuilder.build()) - } - session.setActive(true) - - mediaData = MediaTestUtils.emptyMediaData.copy( - artist = ARTIST, - song = TITLE, - packageName = PACKAGE, - token = session.sessionToken, - device = device, - instanceId = instanceId) + initDeviceMediaData(false, DEVICE_NAME) // Set up recommendation view initRecommendationViewHolderMocks() @@ -293,6 +278,34 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(gutsViewHolder.dismissText).thenReturn(dismissText) } + private fun initDeviceMediaData(shouldShowBroadcastButton: Boolean, name: String) { + device = MediaDeviceData(true, null, name, null, + showBroadcastButton = shouldShowBroadcastButton) + + // Create media session + val metadataBuilder = MediaMetadata.Builder().apply { + putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST) + putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) + } + val playbackBuilder = PlaybackState.Builder().apply { + setState(PlaybackState.STATE_PAUSED, 6000L, 1f) + setActions(PlaybackState.ACTION_PLAY) + } + session = MediaSession(context, SESSION_KEY).apply { + setMetadata(metadataBuilder.build()) + setPlaybackState(playbackBuilder.build()) + } + session.setActive(true) + + mediaData = MediaTestUtils.emptyMediaData.copy( + artist = ARTIST, + song = TITLE, + packageName = PACKAGE, + token = session.sessionToken, + device = device, + instanceId = instanceId) + } + /** * Initialize elements in media view holder */ @@ -1032,6 +1045,28 @@ public class MediaControlPanelTest : SysuiTestCase() { assertThat(seamless.isEnabled()).isFalse() } + @Test + fun bindBroadcastButton() { + initMediaViewHolderMocks() + initDeviceMediaData(true, APP_NAME) + + val mockAvd0 = mock(AnimatedVectorDrawable::class.java) + whenever(mockAvd0.mutate()).thenReturn(mockAvd0) + val semanticActions0 = MediaButton( + playOrPause = MediaAction(mockAvd0, Runnable {}, "play", null) + ) + val state = mediaData.copy(resumption = true, semanticActions = semanticActions0, + isPlaying = false) + player.attachPlayer(viewHolder) + player.bindPlayer(state, PACKAGE) + assertThat(seamlessText.getText()).isEqualTo(APP_NAME) + assertThat(seamless.isEnabled()).isTrue() + + seamless.callOnClick() + + verify(logger).logOpenBroadcastDialog(anyInt(), eq(PACKAGE), eq(instanceId)) + } + /* ***** Guts tests for the player ***** */ @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java index 3e335c5163a9..04b93d79f83b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataCombineLatestTest.java @@ -79,7 +79,7 @@ public class MediaDataCombineLatestTest extends SysuiTestCase { new ArrayList<>(), new ArrayList<>(), null, PACKAGE, null, null, null, true, null, MediaData.PLAYBACK_LOCAL, false, KEY, false, false, false, 0L, InstanceId.fakeInstanceId(-1), -1); - mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME); + mDeviceData = new MediaDeviceData(true, null, DEVICE_NAME, null, false); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt index 18ee79138b52..11fe87d363a0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDeviceManagerTest.kt @@ -16,6 +16,10 @@ package com.android.systemui.media +import android.bluetooth.BluetoothLeBroadcast +import android.bluetooth.BluetoothLeBroadcastMetadata +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager import android.graphics.drawable.Drawable import android.media.MediaRouter2Manager import android.media.RoutingSessionInfo @@ -25,8 +29,12 @@ import android.media.session.MediaSession import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionManager @@ -42,6 +50,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.any import org.mockito.Mockito.mock @@ -57,6 +66,8 @@ private const val PACKAGE = "PKG" private const val SESSION_KEY = "SESSION_KEY" private const val DEVICE_NAME = "DEVICE_NAME" private const val REMOTE_DEVICE_NAME = "REMOTE_DEVICE_NAME" +private const val BROADCAST_APP_NAME = "BROADCAST_APP_NAME" +private const val NORMAL_APP_NAME = "NORMAL_APP_NAME" @SmallTest @RunWith(AndroidTestingRunner::class) @@ -80,6 +91,12 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Mock private lateinit var controller: MediaController @Mock private lateinit var playbackInfo: PlaybackInfo @Mock private lateinit var configurationController: ConfigurationController + @Mock private lateinit var bluetoothLeBroadcast: BluetoothLeBroadcast + @Mock private lateinit var localBluetoothProfileManager: LocalBluetoothProfileManager + @Mock private lateinit var localBluetoothLeBroadcast: LocalBluetoothLeBroadcast + @Mock private lateinit var packageManager: PackageManager + @Mock private lateinit var applicationInfo: ApplicationInfo + private lateinit var localBluetoothManager: LocalBluetoothManager private lateinit var session: MediaSession private lateinit var mediaData: MediaData @JvmField @Rule val mockito = MockitoJUnit.rule() @@ -88,12 +105,15 @@ public class MediaDeviceManagerTest : SysuiTestCase() { fun setUp() { fakeFgExecutor = FakeExecutor(FakeSystemClock()) fakeBgExecutor = FakeExecutor(FakeSystemClock()) + localBluetoothManager = mDependency.injectMockDependency(LocalBluetoothManager::class.java) manager = MediaDeviceManager( + context, controllerFactory, lmmFactory, mr2, muteAwaitFactory, configurationController, + localBluetoothManager, fakeFgExecutor, fakeBgExecutor, dumpster @@ -116,6 +136,7 @@ public class MediaDeviceManagerTest : SysuiTestCase() { token = session.sessionToken) whenever(controllerFactory.create(session.sessionToken)) .thenReturn(controller) + setupLeAudioConfiguration(false) } @After @@ -459,7 +480,8 @@ public class MediaDeviceManagerTest : SysuiTestCase() { @Test fun testRemotePlaybackDeviceOverride() { whenever(route.name).thenReturn(DEVICE_NAME) - val deviceData = MediaDeviceData(false, null, REMOTE_DEVICE_NAME, null) + val deviceData = MediaDeviceData(false, null, REMOTE_DEVICE_NAME, null, + showBroadcastButton = false) val mediaDataWithDevice = mediaData.copy(device = deviceData) // GIVEN media data that already has a device set @@ -474,12 +496,95 @@ public class MediaDeviceManagerTest : SysuiTestCase() { verify(lmm, never()).registerCallback(any()) } + @Test + fun onBroadcastStarted_currentMediaDeviceDataIsBroadcasting() { + val broadcastCallback = setupBroadcastCallback() + setupLeAudioConfiguration(true) + setupBroadcastPackage(BROADCAST_APP_NAME) + broadcastCallback.onBroadcastStarted(1, 1) + + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + + val data = captureDeviceData(KEY) + assertThat(data.showBroadcastButton).isTrue() + assertThat(data.enabled).isTrue() + assertThat(data.name).isEqualTo(context.getString( + R.string.broadcasting_description_is_broadcasting)) + } + + @Test + fun onBroadcastStarted_currentMediaDeviceDataIsNotBroadcasting() { + val broadcastCallback = setupBroadcastCallback() + setupLeAudioConfiguration(true) + setupBroadcastPackage(NORMAL_APP_NAME) + broadcastCallback.onBroadcastStarted(1, 1) + + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + + val data = captureDeviceData(KEY) + assertThat(data.showBroadcastButton).isTrue() + assertThat(data.enabled).isTrue() + assertThat(data.name).isEqualTo(BROADCAST_APP_NAME) + } + + @Test + fun onBroadcastStopped_bluetoothLeBroadcastIsDisabledAndBroadcastingButtonIsGone() { + val broadcastCallback = setupBroadcastCallback() + setupLeAudioConfiguration(false) + broadcastCallback.onBroadcastStopped(1, 1) + + manager.onMediaDataLoaded(KEY, null, mediaData) + fakeBgExecutor.runAllReady() + fakeFgExecutor.runAllReady() + + val data = captureDeviceData(KEY) + assertThat(data.showBroadcastButton).isFalse() + } + fun captureCallback(): LocalMediaManager.DeviceCallback { val captor = ArgumentCaptor.forClass(LocalMediaManager.DeviceCallback::class.java) verify(lmm).registerCallback(captor.capture()) return captor.getValue() } + fun setupBroadcastCallback(): BluetoothLeBroadcast.Callback { + val callback: BluetoothLeBroadcast.Callback = object : BluetoothLeBroadcast.Callback { + override fun onBroadcastStarted(reason: Int, broadcastId: Int) {} + override fun onBroadcastStartFailed(reason: Int) {} + override fun onBroadcastStopped(reason: Int, broadcastId: Int) {} + override fun onBroadcastStopFailed(reason: Int) {} + override fun onPlaybackStarted(reason: Int, broadcastId: Int) {} + override fun onPlaybackStopped(reason: Int, broadcastId: Int) {} + override fun onBroadcastUpdated(reason: Int, broadcastId: Int) {} + override fun onBroadcastUpdateFailed(reason: Int, broadcastId: Int) {} + override fun onBroadcastMetadataChanged(broadcastId: Int, + metadata: BluetoothLeBroadcastMetadata) {} + } + + bluetoothLeBroadcast.registerCallback(fakeFgExecutor, callback) + return callback; + } + + fun setupLeAudioConfiguration(isLeAudio: Boolean) { + whenever(localBluetoothManager.profileManager).thenReturn(localBluetoothProfileManager) + whenever(localBluetoothProfileManager.leAudioBroadcastProfile) + .thenReturn(localBluetoothLeBroadcast) + whenever(localBluetoothLeBroadcast.isEnabled(any())).thenReturn(isLeAudio) + whenever(localBluetoothLeBroadcast.appSourceName).thenReturn(BROADCAST_APP_NAME) + } + + fun setupBroadcastPackage(currentName: String) { + whenever(lmm.packageName).thenReturn(PACKAGE) + whenever(packageManager.getApplicationInfo(eq(PACKAGE), anyInt())) + .thenReturn(applicationInfo) + whenever(packageManager.getApplicationLabel(applicationInfo)).thenReturn(currentName) + context.setMockPackageManager(packageManager) + } + fun captureDeviceData(key: String, oldKey: String? = null): MediaDeviceData { val captor = ArgumentCaptor.forClass(MediaDeviceData::class.java) verify(listener).onMediaDeviceChanged(eq(key), eq(oldKey), captor.capture()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTestUtils.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTestUtils.kt index ae58fe67da23..3d9ed5fe7f7b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaTestUtils.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaTestUtils.kt @@ -20,7 +20,8 @@ class MediaTestUtils { device = null, active = true, resumeAction = null, + isPlaying = false, instanceId = InstanceId.fakeInstanceId(-1), appUid = -1) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt index 0f2c2647f20f..ca3182affcc1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.qs.tiles.ColorInversionTile import com.android.systemui.qs.tiles.DataSaverTile import com.android.systemui.qs.tiles.DeviceControlsTile import com.android.systemui.qs.tiles.DndTile +import com.android.systemui.qs.tiles.DreamTile import com.android.systemui.qs.tiles.FlashlightTile import com.android.systemui.qs.tiles.HotspotTile import com.android.systemui.qs.tiles.InternetTile @@ -89,7 +90,8 @@ private val specMap = mapOf( "wallet" to QuickAccessWalletTile::class.java, "qr_code_scanner" to QRCodeScannerTile::class.java, "onehanded" to OneHandedModeTile::class.java, - "color_correction" to ColorCorrectionTile::class.java + "color_correction" to ColorCorrectionTile::class.java, + "dream" to DreamTile::class.java ) @RunWith(AndroidTestingRunner::class) @@ -129,6 +131,7 @@ class QSFactoryImplTest : SysuiTestCase() { @Mock private lateinit var qrCodeScannerTile: QRCodeScannerTile @Mock private lateinit var oneHandedModeTile: OneHandedModeTile @Mock private lateinit var colorCorrectionTile: ColorCorrectionTile + @Mock private lateinit var dreamTile: DreamTile private lateinit var factory: QSFactoryImpl @@ -171,7 +174,8 @@ class QSFactoryImplTest : SysuiTestCase() { { quickAccessWalletTile }, { qrCodeScannerTile }, { oneHandedModeTile }, - { colorCorrectionTile } + { colorCorrectionTile }, + { dreamTile } ) // When adding/removing tiles, fix also [specMap] } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt index cc472481ad8a..d65901777a73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt @@ -10,6 +10,7 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.internal.logging.testing.UiEventLoggerFake +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.plugins.ActivityStarter @@ -18,6 +19,7 @@ import com.android.systemui.plugins.qs.QSTile import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.qs.QSTileHost import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.BluetoothController import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -25,6 +27,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -84,6 +87,53 @@ class BluetoothTileTest : SysuiTestCase() { assertThat(tile.restrictionChecked).isEqualTo(UserManager.DISALLOW_BLUETOOTH) } + @Test + fun testIcon_whenDisabled_isOffState() { + val state = QSTile.BooleanState() + disableBluetooth() + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off)) + } + + @Test + fun testIcon_whenDisconnected_isOffState() { + val state = QSTile.BooleanState() + enableBluetooth() + setBluetoothDisconnected() + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_off)) + } + + @Test + fun testIcon_whenConnected_isOnState() { + val state = QSTile.BooleanState() + enableBluetooth() + setBluetoothConnected() + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_on)) + } + + @Test + fun testIcon_whenConnecting_isSearchState() { + val state = QSTile.BooleanState() + enableBluetooth() + setBluetoothConnecting() + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_bluetooth_icon_search)) + } + private class FakeBluetoothTile( qsTileHost: QSTileHost, backgroundLooper: Looper, @@ -114,4 +164,27 @@ class BluetoothTileTest : SysuiTestCase() { restrictionChecked = userRestriction } } + + fun enableBluetooth() { + `when`(bluetoothController.isBluetoothEnabled).thenReturn(true) + } + + fun disableBluetooth() { + `when`(bluetoothController.isBluetoothEnabled).thenReturn(false) + } + + fun setBluetoothDisconnected() { + `when`(bluetoothController.isBluetoothConnecting).thenReturn(false) + `when`(bluetoothController.isBluetoothConnected).thenReturn(false) + } + + fun setBluetoothConnected() { + `when`(bluetoothController.isBluetoothConnecting).thenReturn(false) + `when`(bluetoothController.isBluetoothConnected).thenReturn(true) + } + + fun setBluetoothConnecting() { + `when`(bluetoothController.isBluetoothConnected).thenReturn(false) + `when`(bluetoothController.isBluetoothConnecting).thenReturn(true) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java new file mode 100644 index 000000000000..c2a41d5e41fb --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2022 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.qs.tiles; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ComponentName; +import android.os.Handler; +import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.Settings; +import android.service.dreams.IDreamManager; +import android.service.quicksettings.Tile; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSTileHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.util.settings.FakeSettings; +import com.android.systemui.util.settings.SecureSettings; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +public class DreamTileTest extends SysuiTestCase { + + @Mock + private ActivityStarter mActivityStarter; + @Mock + private QSTileHost mHost; + @Mock + private MetricsLogger mMetricsLogger; + @Mock + private QSLogger mQSLogger; + @Mock + private StatusBarStateController mStatusBarStateController; + @Mock + private IDreamManager mDreamManager; + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Mock + private UserTracker mUserTracker; + + private TestableLooper mTestableLooper; + + private DreamTile mTile; + + private SecureSettings mSecureSettings; + + private static final ComponentName COLORS_DREAM_COMPONENT_NAME = new ComponentName( + "com.android.dreams", ".Colors"); + + private static final int DEFAULT_USER = 0; + + private final String mExpectedTileLabel = mContext.getResources().getString( + R.string.quick_settings_screensaver_label); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mSecureSettings = new FakeSettings(); + mTestableLooper = TestableLooper.get(this); + + when(mHost.getUserId()).thenReturn(DEFAULT_USER); + when(mHost.getContext()).thenReturn(mContext); + + mTile = spy(constructTileForTest(true, false)); + + mTestableLooper.processAllMessages(); + mTile.initialize(); + } + + @Test + public void testNotAvailable() throws RemoteException { + // Should not be available if screensaver is disabled + setScreensaverEnabled(false); + + mTile.refreshState(); + mTestableLooper.processAllMessages(); + assertEquals(Tile.STATE_UNAVAILABLE, mTile.getState().state); + + // Should not be available if component is not set + mSecureSettings.putInt(Settings.Secure.SCREENSAVER_ENABLED, 1); + when(mDreamManager.getDreamComponents()).thenReturn(null); + + mTestableLooper.processAllMessages(); + assertEquals(Tile.STATE_UNAVAILABLE, mTile.getState().state); + assertEquals(mExpectedTileLabel, mTile.getState().contentDescription); + } + + @Test + public void testInactiveWhenDreaming() throws RemoteException { + setScreensaverEnabled(true); + + when(mDreamManager.getDreamComponents()).thenReturn(new ComponentName[]{ + COLORS_DREAM_COMPONENT_NAME + }); + when(mDreamManager.isDreaming()).thenReturn(false); + + mTile.refreshState(); + mTestableLooper.processAllMessages(); + assertEquals(Tile.STATE_INACTIVE, mTile.getState().state); + } + + @Test + public void testActive() throws RemoteException { + setScreensaverEnabled(true); + + when(mDreamManager.getDreamComponents()).thenReturn(new ComponentName[]{ + COLORS_DREAM_COMPONENT_NAME + }); + when(mDreamManager.isDreaming()).thenReturn(true); + + mTile.refreshState(); + mTestableLooper.processAllMessages(); + assertEquals(Tile.STATE_ACTIVE, mTile.getState().state); + } + + @Test + public void testClick() throws RemoteException { + // Set the AOSP dream enabled as the base setup. + setScreensaverEnabled(true); + when(mDreamManager.getDreamComponents()).thenReturn(new ComponentName[]{ + COLORS_DREAM_COMPONENT_NAME + }); + when(mDreamManager.isDreaming()).thenReturn(false); + + mTile.refreshState(); + mTestableLooper.processAllMessages(); + assertEquals(Tile.STATE_INACTIVE, mTile.getState().state); + + // Now click + mTile.handleClick(null /* view */); + + verify(mDreamManager).dream(); + + when(mDreamManager.isDreaming()).thenReturn(true); + mTile.refreshState(); + mTestableLooper.processAllMessages(); + assertEquals(Tile.STATE_ACTIVE, mTile.getState().state); + + // Click again to see that other method is called + mTile.handleClick(null /* view */); + + verify(mDreamManager).awaken(); + } + + @Test + public void testContentDescription() { + assertEquals(mExpectedTileLabel, mTile.getContentDescription(null)); + + final String testDreamName = "MyDream"; + assertEquals(mExpectedTileLabel + ", " + testDreamName, + mTile.getContentDescription(testDreamName)); + } + + @Test + public void testUserAvailability() { + DreamTile unsupportedTile = constructTileForTest(false, true); + assertFalse(unsupportedTile.isAvailable()); + + DreamTile supportedTileAllUsers = constructTileForTest(true, false); + + UserHandle systemUserHandle = mock(UserHandle.class); + when(systemUserHandle.isSystem()).thenReturn(true); + + UserHandle nonSystemUserHandle = mock(UserHandle.class); + when(nonSystemUserHandle.isSystem()).thenReturn(false); + + when(mUserTracker.getUserHandle()).thenReturn(systemUserHandle); + assertTrue(supportedTileAllUsers.isAvailable()); + when(mUserTracker.getUserHandle()).thenReturn(nonSystemUserHandle); + assertTrue(supportedTileAllUsers.isAvailable()); + + DreamTile supportedTileOnlySystemUser = constructTileForTest(true, true); + when(mUserTracker.getUserHandle()).thenReturn(systemUserHandle); + assertTrue(supportedTileOnlySystemUser.isAvailable()); + when(mUserTracker.getUserHandle()).thenReturn(nonSystemUserHandle); + assertFalse(supportedTileOnlySystemUser.isAvailable()); + } + + private void setScreensaverEnabled(boolean enabled) { + mSecureSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, enabled ? 1 : 0, + DEFAULT_USER); + } + + private DreamTile constructTileForTest(boolean dreamSupported, + boolean dreamOnlyEnabledForSystemUser) { + return new DreamTile( + mHost, + mTestableLooper.getLooper(), + new Handler(mTestableLooper.getLooper()), + new FalsingManagerFake(), + mMetricsLogger, + mStatusBarStateController, + mActivityStarter, + mQSLogger, + mDreamManager, + mSecureSettings, + mBroadcastDispatcher, + mUserTracker, + dreamSupported, dreamOnlyEnabledForSystemUser); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java index 55c51b255ac7..e9dfd3ed182b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java @@ -36,9 +36,11 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSTileHost; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.DeviceStateRotationLockSettingController; import com.android.systemui.statusbar.policy.RotationLockController; @@ -194,6 +196,26 @@ public class RotationLockTileTest extends SysuiTestCase { assertEquals("", mLockTile.getState().secondaryLabel.toString()); } + @Test + public void testIcon_whenDisabled_isOffState() { + QSTile.BooleanState state = new QSTile.BooleanState(); + disableAutoRotation(); + + mLockTile.handleUpdateState(state, /* arg= */ null); + + assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_off)); + } + + @Test + public void testIcon_whenEnabled_isOnState() { + QSTile.BooleanState state = new QSTile.BooleanState(); + enableAutoRotation(); + + mLockTile.handleUpdateState(state, /* arg= */ null); + + assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_auto_rotate_icon_on)); + } + private void enableAutoRotation() { when(mRotationPolicyWrapper.isRotationLocked()).thenReturn(false); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt index 32314159f865..a4a89a4c67f2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2022 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.shared.animation import android.testing.AndroidTestingRunner @@ -7,31 +21,24 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate -import com.android.systemui.unfold.UnfoldTransitionProgressProvider -import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.TestUnfoldTransitionProvider import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { - @Mock private lateinit var progressProvider: UnfoldTransitionProgressProvider + private val progressProvider = TestUnfoldTransitionProvider() @Mock private lateinit var parent: ViewGroup - @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener> - private lateinit var animator: UnfoldConstantTranslateAnimator - private lateinit var progressListener: TransitionProgressListener private val viewsIdToRegister = setOf( @@ -46,17 +53,14 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider) animator.init(parent, MAX_TRANSLATION) - - verify(progressProvider).addCallback(progressListenerCaptor.capture()) - progressListener = progressListenerCaptor.value } @Test fun onTransition_noMatchingIds() { // GIVEN no views matching any ids // WHEN the transition starts - progressListener.onTransitionStarted() - progressListener.onTransitionProgress(.1f) + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(.1f) // THEN nothing... no exceptions } @@ -86,22 +90,22 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { // Compare values as ints because -0f != 0f // WHEN the transition starts - progressListener.onTransitionStarted() - progressListener.onTransitionProgress(0f) + progressProvider.onTransitionStarted() + progressProvider.onTransitionProgress(0f) list.forEach { (view, direction) -> assertEquals((-MAX_TRANSLATION * direction).toInt(), view.translationX.toInt()) } // WHEN the transition progresses, translation is updated - progressListener.onTransitionProgress(.5f) + progressProvider.onTransitionProgress(.5f) list.forEach { (view, direction) -> assertEquals((-MAX_TRANSLATION / 2f * direction).toInt(), view.translationX.toInt()) } // WHEN the transition ends, translation is completed - progressListener.onTransitionProgress(1f) - progressListener.onTransitionFinished() + progressProvider.onTransitionProgress(1f) + progressProvider.onTransitionFinished() list.forEach { (view, _) -> assertEquals(0, view.translationX.toInt()) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt index 2cfe6be5c6b2..2f0f0a0a1b8f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt @@ -26,8 +26,9 @@ import android.view.View import android.view.ViewGroup import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.dreams.smartspace.DreamsSmartspaceController +import com.android.systemui.dreams.smartspace.DreamSmartspaceController import com.android.systemui.plugins.BcSmartspaceDataPlugin +import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceView import com.android.systemui.plugins.FalsingManager import com.android.systemui.smartspace.dagger.SmartspaceViewComponent import com.android.systemui.util.concurrency.Execution @@ -39,6 +40,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Spy import org.mockito.Mockito import org.mockito.Mockito.`when` import org.mockito.Mockito.verify @@ -74,8 +76,8 @@ class DreamSmartspaceControllerTest : SysuiTestCase() { @Mock private lateinit var precondition: SmartspacePrecondition - @Mock - private lateinit var smartspaceView: BcSmartspaceDataPlugin.SmartspaceView + @Spy + private var smartspaceView: SmartspaceView = TestView(context) @Mock private lateinit var listener: BcSmartspaceDataPlugin.SmartspaceTargetListener @@ -83,6 +85,8 @@ class DreamSmartspaceControllerTest : SysuiTestCase() { @Mock private lateinit var session: SmartspaceSession + private lateinit var controller: DreamSmartspaceController + @Before fun setup() { MockitoAnnotations.initMocks(this) @@ -90,6 +94,9 @@ class DreamSmartspaceControllerTest : SysuiTestCase() { .thenReturn(viewComponent) `when`(viewComponent.getView()).thenReturn(smartspaceView) `when`(smartspaceManager.createSmartspaceSession(any())).thenReturn(session) + + controller = DreamSmartspaceController(context, smartspaceManager, execution, uiExecutor, + viewComponentFactory, precondition, Optional.of(targetFilter), Optional.of(plugin)) } /** @@ -97,10 +104,6 @@ class DreamSmartspaceControllerTest : SysuiTestCase() { */ @Test fun testConnectOnListen() { - val controller = DreamsSmartspaceController(context, - smartspaceManager, execution, uiExecutor, viewComponentFactory, precondition, - Optional.of(targetFilter), Optional.of(plugin)) - `when`(precondition.conditionsMet()).thenReturn(true) controller.addListener(listener) @@ -130,8 +133,7 @@ class DreamSmartspaceControllerTest : SysuiTestCase() { * A class which implements SmartspaceView and extends View. This is mocked to provide the right * object inheritance and interface implementation used in DreamSmartspaceController */ - private class TestView(context: Context?) : View(context), - BcSmartspaceDataPlugin.SmartspaceView { + private class TestView(context: Context?) : View(context), SmartspaceView { override fun registerDataProvider(plugin: BcSmartspaceDataPlugin?) {} override fun setPrimaryTextColor(color: Int) {} @@ -160,11 +162,6 @@ class DreamSmartspaceControllerTest : SysuiTestCase() { */ @Test fun testConnectOnViewCreate() { - val controller = DreamsSmartspaceController(context, - smartspaceManager, execution, uiExecutor, viewComponentFactory, precondition, - Optional.of(targetFilter), - Optional.of(plugin)) - `when`(precondition.conditionsMet()).thenReturn(true) controller.buildAndConnectView(Mockito.mock(ViewGroup::class.java)) @@ -183,4 +180,16 @@ class DreamSmartspaceControllerTest : SysuiTestCase() { verify(session).close() } + + /** + * Ensures setIsDreaming(true) is called when the view is built. + */ + @Test + fun testSetIsDreamingTrueOnViewCreate() { + `when`(precondition.conditionsMet()).thenReturn(true) + + controller.buildAndConnectView(Mockito.mock(ViewGroup::class.java)) + + verify(smartspaceView).setIsDreaming(true) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt index 699f77f9b7bb..2ee31265ff7b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt @@ -59,6 +59,7 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import java.util.ArrayList +import java.util.function.Consumer import org.mockito.Mockito.`when` as whenever @SmallTest @@ -75,6 +76,7 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { private lateinit var mBeforeFinalizeFilterListener: OnBeforeFinalizeFilterListener private lateinit var mOnHeadsUpChangedListener: OnHeadsUpChangedListener private lateinit var mNotifSectioner: NotifSectioner + private lateinit var mActionPressListener: Consumer<NotificationEntry> private val mNotifPipeline: NotifPipeline = mock() private val mLogger = HeadsUpCoordinatorLogger(logcatLogBuffer(), verbose = true) @@ -131,6 +133,9 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { mOnHeadsUpChangedListener = withArgCaptor { verify(mHeadsUpManager).addListener(capture()) } + mActionPressListener = withArgCaptor { + verify(mRemoteInputManager).addActionPressListener(capture()) + } given(mHeadsUpManager.allEntries).willAnswer { mHuns.stream() } given(mHeadsUpManager.isAlerting(anyString())).willAnswer { invocation -> val key = invocation.getArgument<String>(0) @@ -199,6 +204,19 @@ class HeadsUpCoordinatorTest : SysuiTestCase() { } @Test + fun hunExtensionCancelledWhenHunActionPressed() { + whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true) + addHUN(mEntry) + whenever(mHeadsUpManager.canRemoveImmediately(anyString())).thenReturn(false) + whenever(mHeadsUpManager.getEarliestRemovalTime(anyString())).thenReturn(1000L) + assertTrue(mNotifLifetimeExtender.maybeExtendLifetime(mEntry, 0)) + mActionPressListener.accept(mEntry) + mExecutor.advanceClockToLast() + mExecutor.runAllReady() + verify(mHeadsUpManager, times(1)).removeNotification(eq(mEntry.key), eq(true)) + } + + @Test fun testCancelUpdatedStickyNotification() { whenever(mHeadsUpManager.isSticky(anyString())).thenReturn(true) addHUN(mEntry) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java index f3aa20ba4527..2f37f8928e79 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/VisualStabilityCoordinatorTest.java @@ -136,7 +136,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Test public void testScreenOff_groupAndSectionChangesAllowed() { // GIVEN screen is off, panel isn't expanded and device isn't pulsing - setScreenOn(false); + setFullyDozed(true); + setSleepy(true); setPanelExpanded(false); setPulsing(false); @@ -149,9 +150,42 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { } @Test + public void testScreenTurningOff_groupAndSectionChangesNotAllowed() { + // GIVEN the screen is turning off (sleepy but partially dozed) + setFullyDozed(false); + setSleepy(true); + setPanelExpanded(true); + setPulsing(false); + + // THEN group changes are NOT allowed + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry)); + + // THEN section changes are NOT allowed + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test + public void testScreenTurningOn_groupAndSectionChangesNotAllowed() { + // GIVEN the screen is turning on (still fully dozed, not sleepy) + setFullyDozed(true); + setSleepy(false); + setPanelExpanded(true); + setPulsing(false); + + // THEN group changes are NOT allowed + assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); + assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry)); + + // THEN section changes are NOT allowed + assertFalse(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); + } + + @Test public void testPanelNotExpanded_groupAndSectionChangesAllowed() { // GIVEN screen is on but the panel isn't expanded and device isn't pulsing - setScreenOn(true); + setFullyDozed(false); + setSleepy(false); setPanelExpanded(false); setPulsing(false); @@ -166,7 +200,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Test public void testPanelExpanded_groupAndSectionChangesNotAllowed() { // GIVEN the panel true expanded and device isn't pulsing - setScreenOn(true); + setFullyDozed(false); + setSleepy(false); setPanelExpanded(true); setPulsing(false); @@ -181,7 +216,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Test public void testPulsing_screenOff_groupAndSectionChangesNotAllowed() { // GIVEN the device is pulsing and screen is off - setScreenOn(false); + setFullyDozed(true); + setSleepy(true); setPulsing(true); // THEN group changes are NOT allowed @@ -195,7 +231,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Test public void testPulsing_panelNotExpanded_groupAndSectionChangesNotAllowed() { // GIVEN the device is pulsing and screen is off with the panel not expanded - setScreenOn(false); + setFullyDozed(true); + setSleepy(true); setPanelExpanded(false); setPulsing(true); @@ -211,7 +248,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { public void testOverrideReorderingSuppression_onlySectionChangesAllowed() { // GIVEN section changes typically wouldn't be allowed because the panel is expanded and // we're not pulsing - setScreenOn(true); + setFullyDozed(false); + setSleepy(false); setPanelExpanded(true); setPulsing(true); @@ -233,7 +271,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Test public void testTemporarilyAllowSectionChanges_callsInvalidate() { // GIVEN section changes typically wouldn't be allowed because the panel is expanded - setScreenOn(true); + setFullyDozed(false); + setSleepy(false); setPanelExpanded(true); setPulsing(false); @@ -247,7 +286,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Test public void testTemporarilyAllowSectionChanges_noInvalidationCalled() { // GIVEN section changes typically WOULD be allowed - setScreenOn(false); + setFullyDozed(true); + setSleepy(true); setPanelExpanded(false); setPulsing(false); @@ -261,7 +301,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Test public void testTemporarilyAllowSectionChangesTimeout() { // GIVEN section changes typically WOULD be allowed - setScreenOn(false); + setFullyDozed(true); + setSleepy(true); setPanelExpanded(false); setPulsing(false); assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); @@ -292,7 +333,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Test public void testTemporarilyAllowSectionChanges_isPulsingChangeBeforeTimeout() { // GIVEN section changes typically wouldn't be allowed because the device is pulsing - setScreenOn(false); + setFullyDozed(true); + setSleepy(true); setPanelExpanded(false); setPulsing(true); @@ -315,8 +357,11 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { // WHEN device isn't pulsing anymore setPulsing(false); - // WHEN screen isn't on - setScreenOn(false); + // WHEN fully dozed + setFullyDozed(true); + + // WHEN sleepy + setSleepy(true); // WHEN panel isn't expanded setPanelExpanded(false); @@ -330,7 +375,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { public void testNotSuppressingGroupChangesAnymore_invalidationCalled() { // GIVEN visual stability is being maintained b/c panel is expanded setPulsing(false); - setScreenOn(true); + setFullyDozed(false); + setSleepy(false); setPanelExpanded(true); assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); @@ -399,7 +445,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { public void testNotSuppressingEntryReorderingAnymoreWillInvalidate() { // GIVEN visual stability is being maintained b/c panel is expanded setPulsing(false); - setScreenOn(true); + setFullyDozed(false); + setSleepy(false); setPanelExpanded(true); assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry)); @@ -417,7 +464,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { public void testQueryingEntryReorderingButNotReportingReorderSuppressedDoesNotInvalidate() { // GIVEN visual stability is being maintained b/c panel is expanded setPulsing(false); - setScreenOn(true); + setFullyDozed(false); + setSleepy(false); setPanelExpanded(true); assertFalse(mNotifStabilityManager.isEntryReorderingAllowed(mEntry)); @@ -432,7 +480,8 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { @Test public void testHeadsUp_allowedToChangeGroupAndSection() { // GIVEN group + section changes disallowed - setScreenOn(true); + setFullyDozed(false); + setSleepy(false); setPanelExpanded(true); setPulsing(true); assertFalse(mNotifStabilityManager.isGroupChangeAllowed(mEntry)); @@ -462,11 +511,16 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { mStatusBarStateListener.onPulsingChanged(pulsing); } - private void setScreenOn(boolean screenOn) { - if (screenOn) { - mWakefulnessObserver.onStartedWakingUp(); - } else { + private void setFullyDozed(boolean fullyDozed) { + float dozeAmount = fullyDozed ? 1 : 0; + mStatusBarStateListener.onDozeAmountChanged(dozeAmount, dozeAmount); + } + + private void setSleepy(boolean sleepy) { + if (sleepy) { mWakefulnessObserver.onFinishedGoingToSleep(); + } else { + mWakefulnessObserver.onStartedWakingUp(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java index 8a2dc263cf36..7b40ba87b494 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImplTest.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.interruption; import static android.app.Notification.FLAG_BUBBLE; +import static android.app.Notification.GROUP_ALERT_SUMMARY; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; @@ -431,6 +432,17 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } /** + * Test that notification can bubble even if it is a child in a group and group settings are + * set to alert only for summary notifications. + */ + @Test + public void testShouldBubbleUp_notifInGroupWithOnlySummaryAlerts() { + ensureStateForBubbleUp(); + NotificationEntry bubble = createBubble("testgroup", GROUP_ALERT_SUMMARY); + assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(bubble)).isTrue(); + } + + /** * If the notification doesn't have permission to bubble, it shouldn't bubble. */ @Test @@ -497,16 +509,27 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { } private NotificationEntry createBubble() { + return createBubble(null, null); + } + + private NotificationEntry createBubble(String groupKey, Integer groupAlert) { Notification.BubbleMetadata data = new Notification.BubbleMetadata.Builder( PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_MUTABLE), Icon.createWithResource(mContext.getResources(), R.drawable.android)) .build(); - Notification n = new Notification.Builder(getContext(), "a") + Notification.Builder nb = new Notification.Builder(getContext(), "a") .setContentTitle("title") .setContentText("content text") - .setBubbleMetadata(data) - .build(); + .setBubbleMetadata(data); + if (groupKey != null) { + nb.setGroup(groupKey); + nb.setGroupSummary(false); + } + if (groupAlert != null) { + nb.setGroupAlertBehavior(groupAlert); + } + Notification n = nb.build(); n.flags |= FLAG_BUBBLE; return new NotificationEntryBuilder() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt index 9ab88dc2d764..ba29e953c73d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt @@ -136,6 +136,7 @@ class PhoneStatusBarViewControllerTest : SysuiTestCase() { private class UnfoldConfig : UnfoldTransitionConfig { override var isEnabled: Boolean = false override var isHingeAngleEnabled: Boolean = false + override val halfFoldedTimeoutMillis: Int = 0 } private class TestTouchEventHandler : PhoneStatusBarView.TouchEventHandler { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 4f2abf263a9b..0c1d04253bf5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -61,7 +61,9 @@ import com.google.common.truth.Truth; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.Optional; @@ -97,6 +99,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private LatencyTracker mLatencyTracker; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback; @Before public void setUp() { @@ -136,6 +139,11 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mBypassController); when(mKeyguardStateController.isOccluded()).thenReturn(false); mStatusBarKeyguardViewManager.show(null); + ArgumentCaptor<KeyguardBouncer.BouncerExpansionCallback> callbackArgumentCaptor = + ArgumentCaptor.forClass(KeyguardBouncer.BouncerExpansionCallback.class); + verify(mKeyguardBouncerFactory).create(any(ViewGroup.class), + callbackArgumentCaptor.capture()); + mBouncerExpansionCallback = callbackArgumentCaptor.getValue(); } @Test @@ -441,4 +449,24 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { return new PanelExpansionChangeEvent( fraction, expanded, tracking, /* dragDownPxAmount= */ 0f); } + + @Test + public void testReportBouncerOnDreamWhenVisible() { + mBouncerExpansionCallback.onVisibilityChanged(true); + verify(mCentralSurfaces).setBouncerShowingOverDream(false); + Mockito.clearInvocations(mCentralSurfaces); + when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true); + mBouncerExpansionCallback.onVisibilityChanged(true); + verify(mCentralSurfaces).setBouncerShowingOverDream(true); + } + + @Test + public void testReportBouncerOnDreamWhenNotVisible() { + mBouncerExpansionCallback.onVisibilityChanged(false); + verify(mCentralSurfaces).setBouncerShowingOverDream(false); + Mockito.clearInvocations(mCentralSurfaces); + when(mDreamOverlayStateController.isOverlayActive()).thenReturn(true); + mBouncerExpansionCallback.onVisibilityChanged(false); + verify(mCentralSurfaces).setBouncerShowingOverDream(false); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt index 6bd8b98f70e1..e0bf9e7b0081 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.policy import android.app.IActivityManager +import android.app.NotificationManager import android.app.admin.DevicePolicyManager import android.content.BroadcastReceiver import android.content.Context @@ -38,7 +39,9 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.util.LatencyTracker import com.android.internal.util.UserIcons +import com.android.systemui.GuestResetOrExitSessionReceiver import com.android.systemui.GuestResumeSessionReceiver +import com.android.systemui.GuestSessionNotification import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator @@ -102,6 +105,11 @@ class UserSwitcherControllerTest : SysuiTestCase() { @Mock private lateinit var threadedRenderer: ThreadedRenderer @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator @Mock private lateinit var globalSettings: GlobalSettings + @Mock private lateinit var guestSessionNotification: GuestSessionNotification + @Mock private lateinit var guestResetOrExitSessionReceiver: GuestResetOrExitSessionReceiver + private lateinit var resetSessionDialogFactory: + GuestResumeSessionReceiver.ResetSessionDialog.Factory + private lateinit var guestResumeSessionReceiver: GuestResumeSessionReceiver private lateinit var testableLooper: TestableLooper private lateinit var bgExecutor: FakeExecutor private lateinit var longRunningExecutor: FakeExecutor @@ -133,9 +141,28 @@ class UserSwitcherControllerTest : SysuiTestCase() { com.android.internal.R.bool.config_guestUserAutoCreated, false) mContext.addMockSystemService(Context.FACE_SERVICE, mock(FaceManager::class.java)) + mContext.addMockSystemService(Context.NOTIFICATION_SERVICE, + mock(NotificationManager::class.java)) mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager::class.java)) + resetSessionDialogFactory = object : GuestResumeSessionReceiver.ResetSessionDialog.Factory { + override fun create(userId: Int): GuestResumeSessionReceiver.ResetSessionDialog { + return GuestResumeSessionReceiver.ResetSessionDialog( + mContext, + mock(UserSwitcherController::class.java), + uiEventLogger, + userId + ) + } + } + + guestResumeSessionReceiver = GuestResumeSessionReceiver(userTracker, + secureSettings, + broadcastDispatcher, + guestSessionNotification, + resetSessionDialogFactory) + `when`(userManager.canAddMoreUsers(eq(UserManager.USER_TYPE_FULL_SECONDARY))) .thenReturn(true) `when`(notificationShadeWindowView.context).thenReturn(context) @@ -198,7 +225,9 @@ class UserSwitcherControllerTest : SysuiTestCase() { interactionJankMonitor, latencyTracker, dumpManager, - dialogLaunchAnimator) + dialogLaunchAnimator, + guestResumeSessionReceiver, + guestResetOrExitSessionReceiver) userSwitcherController.init(notificationShadeWindowView) } @@ -288,7 +317,10 @@ class UserSwitcherControllerTest : SysuiTestCase() { .getButton(DialogInterface.BUTTON_POSITIVE).performClick() testableLooper.processAllMessages() assertEquals(1, uiEventLogger.numLogs()) - assertEquals(QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id, uiEventLogger.eventId(0)) + assertTrue( + QSUserSwitcherEvent.QS_USER_GUEST_REMOVE.id == uiEventLogger.eventId(0) || + QSUserSwitcherEvent.QS_USER_SWITCH.id == uiEventLogger.eventId(0) + ) } @Test @@ -350,7 +382,7 @@ class UserSwitcherControllerTest : SysuiTestCase() { userSwitcherController.onUserListItemClicked(currentGuestUserRecord, null) assertNotNull(userSwitcherController.mExitGuestDialog) userSwitcherController.mExitGuestDialog - .getButton(DialogInterface.BUTTON_NEGATIVE).performClick() + .getButton(DialogInterface.BUTTON_NEUTRAL).performClick() testableLooper.processAllMessages() assertEquals(0, uiEventLogger.numLogs()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt index 8076b4eda883..39e4e6446d02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt @@ -25,28 +25,20 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING -import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate +import com.android.systemui.unfold.util.TestFoldStateProvider import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Captor -import org.mockito.Mock -import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @SmallTest class FoldStateLoggingProviderTest : SysuiTestCase() { - @Captor - private lateinit var foldUpdatesListener: ArgumentCaptor<FoldStateProvider.FoldUpdatesListener> - - @Mock private lateinit var foldStateProvider: FoldStateProvider - + private val testFoldStateProvider = TestFoldStateProvider() private val fakeClock = FakeSystemClock() private lateinit var foldStateLoggingProvider: FoldStateLoggingProvider @@ -65,12 +57,10 @@ class FoldStateLoggingProviderTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) foldStateLoggingProvider = - FoldStateLoggingProviderImpl(foldStateProvider, fakeClock).apply { + FoldStateLoggingProviderImpl(testFoldStateProvider, fakeClock).apply { addCallback(foldStateLoggingListener) init() } - - verify(foldStateProvider).addCallback(foldUpdatesListener.capture()) } @Test @@ -183,10 +173,10 @@ class FoldStateLoggingProviderTest : SysuiTestCase() { fun uninit_removesCallback() { foldStateLoggingProvider.uninit() - verify(foldStateProvider).removeCallback(foldUpdatesListener.value) + assertThat(testFoldStateProvider.hasListeners).isFalse() } private fun sendFoldUpdate(@FoldUpdate foldUpdate: Int) { - foldUpdatesListener.value.onFoldUpdate(foldUpdate) + testFoldStateProvider.sendFoldUpdate(foldUpdate) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt new file mode 100644 index 000000000000..ab450e2506f9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 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.unfold.config + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + + +/** + * A test that checks that we load correct resources in + * ResourceUnfoldTransitionConfig as we use strings there instead of R constants. + * Internal Android resource constants are not available in public APIs, + * so we can't use them there directly. + */ +@RunWith(AndroidTestingRunner::class) +@SmallTest +class ResourceUnfoldTransitionConfigTest : SysuiTestCase() { + + private val config = ResourceUnfoldTransitionConfig() + + @Test + fun testIsEnabled() { + assertThat(config.isEnabled).isEqualTo(mContext.resources + .getBoolean(com.android.internal.R.bool.config_unfoldTransitionEnabled)) + } + + @Test + fun testHingeAngleEnabled() { + assertThat(config.isHingeAngleEnabled).isEqualTo(mContext.resources + .getBoolean(com.android.internal.R.bool.config_unfoldTransitionHingeAngle)) + } + + @Test + fun testHalfFoldedTimeout() { + assertThat(config.halfFoldedTimeoutMillis).isEqualTo(mContext.resources + .getInteger(com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt index 1f1f88b07ea1..87fca1f23f1a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt @@ -16,82 +16,69 @@ package com.android.systemui.unfold.updates -import android.app.ActivityManager -import android.app.ActivityManager.RunningTaskInfo -import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME -import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD -import android.app.WindowConfiguration.ActivityType -import android.hardware.devicestate.DeviceStateManager -import android.hardware.devicestate.DeviceStateManager.FoldStateListener import android.os.Handler import android.testing.AndroidTestingRunner import androidx.core.util.Consumer import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig +import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider +import com.android.systemui.unfold.updates.FoldProvider.FoldCallback import com.android.systemui.unfold.updates.hinge.HingeAngleProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener -import com.android.systemui.unfold.util.FoldableDeviceStates -import com.android.systemui.unfold.util.FoldableTestUtils import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +import java.util.concurrent.Executor +import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @SmallTest class DeviceFoldStateProviderTest : SysuiTestCase() { - @Mock private lateinit var hingeAngleProvider: HingeAngleProvider - - @Mock private lateinit var screenStatusProvider: ScreenStatusProvider - - @Mock private lateinit var deviceStateManager: DeviceStateManager - - @Mock private lateinit var activityManager: ActivityManager - - @Mock private lateinit var handler: Handler - - @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener> + @Mock + private lateinit var activityTypeProvider: ActivityManagerActivityTypeProvider - @Captor private lateinit var screenOnListenerCaptor: ArgumentCaptor<ScreenListener> + @Mock + private lateinit var handler: Handler - @Captor private lateinit var hingeAngleCaptor: ArgumentCaptor<Consumer<Float>> + private val foldProvider = TestFoldProvider() + private val screenOnStatusProvider = TestScreenOnStatusProvider() + private val testHingeAngleProvider = TestHingeAngleProvider() private lateinit var foldStateProvider: DeviceFoldStateProvider private val foldUpdates: MutableList<Int> = arrayListOf() private val hingeAngleUpdates: MutableList<Float> = arrayListOf() - private lateinit var deviceStates: FoldableDeviceStates - private var scheduledRunnable: Runnable? = null private var scheduledRunnableDelay: Long? = null @Before fun setUp() { MockitoAnnotations.initMocks(this) - overrideResource( - com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout, - HALF_OPENED_TIMEOUT_MILLIS.toInt()) - deviceStates = FoldableTestUtils.findDeviceStates(context) + + val config = object : UnfoldTransitionConfig by ResourceUnfoldTransitionConfig() { + override val halfFoldedTimeoutMillis: Int + get() = HALF_OPENED_TIMEOUT_MILLIS.toInt() + } foldStateProvider = DeviceFoldStateProvider( - context, - hingeAngleProvider, - screenStatusProvider, - deviceStateManager, - activityManager, + config, + testHingeAngleProvider, + screenOnStatusProvider, + foldProvider, + activityTypeProvider, context.mainExecutor, - handler) + handler + ) foldStateProvider.addCallback( object : FoldStateProvider.FoldUpdatesListener { @@ -105,10 +92,6 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { }) foldStateProvider.start() - verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture()) - verify(screenStatusProvider).addCallback(screenOnListenerCaptor.capture()) - verify(hingeAngleProvider).addCallback(hingeAngleCaptor.capture()) - whenever(handler.postDelayed(any<Runnable>(), any())).then { invocationOnMock -> scheduledRunnable = invocationOnMock.getArgument<Runnable>(0) scheduledRunnableDelay = invocationOnMock.getArgument<Long>(1) @@ -125,7 +108,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { } // By default, we're on launcher. - setupForegroundActivityType(ACTIVITY_TYPE_HOME) + setupForegroundActivityType(isHomeActivity = true) } @Test @@ -146,14 +129,14 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fun testOnFolded_stopsHingeAngleProvider() { setFoldState(folded = true) - verify(hingeAngleProvider).stop() + assertThat(testHingeAngleProvider.isStarted).isFalse() } @Test fun testOnUnfolded_startsHingeAngleProvider() { setFoldState(folded = false) - verify(hingeAngleProvider).start() + assertThat(testHingeAngleProvider.isStarted).isTrue() } @Test @@ -310,7 +293,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileNotOnLauncher_doesNotTriggerBeforeThreshold() { - setupForegroundActivityType(ACTIVITY_TYPE_STANDARD) + setupForegroundActivityType(isHomeActivity = false) sendHingeAngleEvent(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -319,8 +302,28 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { } @Test + fun startClosingEvent_whileActivityTypeNotAvailable_triggerBeforeThreshold() { + setupForegroundActivityType(isHomeActivity = null) + sendHingeAngleEvent(180) + + sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_whileOnLauncher_doesTriggerBeforeThreshold() { + setupForegroundActivityType(isHomeActivity = true) + sendHingeAngleEvent(180) + + sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test fun startClosingEvent_whileNotOnLauncher_triggersAfterThreshold() { - setupForegroundActivityType(ACTIVITY_TYPE_STANDARD) + setupForegroundActivityType(isHomeActivity = false) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1) @@ -328,9 +331,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } - private fun setupForegroundActivityType(@ActivityType type: Int) { - val taskInfo = RunningTaskInfo().apply { topActivityType = type } - whenever(activityManager.getRunningTasks(1)).thenReturn(listOf(taskInfo)) + private fun setupForegroundActivityType(isHomeActivity: Boolean?) { + whenever(activityTypeProvider.isHomeActivity).thenReturn(isHomeActivity) } private fun simulateTimeout(waitTime: Long = HALF_OPENED_TIMEOUT_MILLIS) { @@ -348,16 +350,72 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { } private fun setFoldState(folded: Boolean) { - val state = if (folded) deviceStates.folded else deviceStates.unfolded - foldStateListenerCaptor.value.onStateChanged(state) + foldProvider.notifyFolded(folded) } private fun fireScreenOnEvent() { - screenOnListenerCaptor.value.onScreenTurnedOn() + screenOnStatusProvider.notifyScreenTurnedOn() } private fun sendHingeAngleEvent(angle: Int) { - hingeAngleCaptor.value.accept(angle.toFloat()) + testHingeAngleProvider.notifyAngle(angle.toFloat()) + } + + private class TestFoldProvider : FoldProvider { + private val callbacks = arrayListOf<FoldCallback>() + + override fun registerCallback(callback: FoldCallback, executor: Executor) { + callbacks += callback + } + + override fun unregisterCallback(callback: FoldCallback) { + callbacks -= callback + } + + fun notifyFolded(isFolded: Boolean) { + callbacks.forEach { it.onFoldUpdated(isFolded) } + } + } + + private class TestScreenOnStatusProvider : ScreenStatusProvider { + private val callbacks = arrayListOf<ScreenListener>() + + override fun addCallback(listener: ScreenListener) { + callbacks += listener + } + + override fun removeCallback(listener: ScreenListener) { + callbacks -= listener + } + + fun notifyScreenTurnedOn() { + callbacks.forEach { it.onScreenTurnedOn() } + } + } + + private class TestHingeAngleProvider : HingeAngleProvider { + private val callbacks = arrayListOf<Consumer<Float>>() + var isStarted: Boolean = false + + override fun start() { + isStarted = true; + } + + override fun stop() { + isStarted = false; + } + + override fun addCallback(listener: Consumer<Float>) { + callbacks += listener + } + + override fun removeCallback(listener: Consumer<Float>) { + callbacks -= listener + } + + fun notifyAngle(angle: Float) { + callbacks.forEach { it.accept(angle) } + } } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt index a3f17aa5ba55..b2cedbf8d606 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt @@ -20,18 +20,18 @@ import android.view.IRotationWatcher import android.view.IWindowManager import android.view.Surface import androidx.test.filters.SmallTest -import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.SysuiTestCase +import com.android.systemui.unfold.TestUnfoldTransitionProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.util.mockito.any -import com.android.systemui.SysuiTestCase import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock -import org.mockito.Mockito.verify import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never +import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -41,16 +41,13 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { @Mock lateinit var windowManager: IWindowManager - @Mock - lateinit var sourceProvider: UnfoldTransitionProgressProvider + private val sourceProvider = TestUnfoldTransitionProvider() @Mock lateinit var transitionListener: TransitionProgressListener lateinit var progressProvider: NaturalRotationUnfoldProgressProvider - private val sourceProviderListenerCaptor = - ArgumentCaptor.forClass(TransitionProgressListener::class.java) private val rotationWatcherCaptor = ArgumentCaptor.forClass(IRotationWatcher.Stub::class.java) @@ -66,7 +63,6 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { progressProvider.init() - verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture()) verify(windowManager).watchRotation(rotationWatcherCaptor.capture(), any()) progressProvider.addCallback(transitionListener) @@ -76,7 +72,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { fun testNaturalRotation0_sendTransitionStartedEvent_eventReceived() { onRotationChanged(Surface.ROTATION_0) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() verify(transitionListener).onTransitionStarted() } @@ -85,7 +81,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { fun testNaturalRotation0_sendTransitionProgressEvent_eventReceived() { onRotationChanged(Surface.ROTATION_0) - source.onTransitionProgress(0.5f) + sourceProvider.onTransitionProgress(0.5f) verify(transitionListener).onTransitionProgress(0.5f) } @@ -94,7 +90,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { fun testNotNaturalRotation90_sendTransitionStartedEvent_eventNotReceived() { onRotationChanged(Surface.ROTATION_90) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() verify(transitionListener, never()).onTransitionStarted() } @@ -103,7 +99,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { fun testNaturalRotation90_sendTransitionProgressEvent_eventNotReceived() { onRotationChanged(Surface.ROTATION_90) - source.onTransitionProgress(0.5f) + sourceProvider.onTransitionProgress(0.5f) verify(transitionListener, never()).onTransitionProgress(0.5f) } @@ -111,7 +107,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { @Test fun testRotationBecameUnnaturalDuringTransition_sendsTransitionFinishedEvent() { onRotationChanged(Surface.ROTATION_0) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() clearInvocations(transitionListener) onRotationChanged(Surface.ROTATION_90) @@ -122,7 +118,7 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { @Test fun testRotationBecameNaturalDuringTransition_sendsTransitionStartedEvent() { onRotationChanged(Surface.ROTATION_90) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() clearInvocations(transitionListener) onRotationChanged(Surface.ROTATION_0) @@ -133,7 +129,4 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { private fun onRotationChanged(rotation: Int) { rotationWatcherCaptor.value.onRotationChanged(rotation) } - - private val source: TransitionProgressListener - get() = sourceProviderListenerCaptor.value } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt index db7a85166807..fc2a78a5ee7f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt @@ -21,6 +21,7 @@ import android.database.ContentObserver import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.unfold.TestUnfoldTransitionProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.util.mockito.any @@ -41,15 +42,11 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { lateinit var contentResolver: ContentResolver @Mock - lateinit var sourceProvider: UnfoldTransitionProgressProvider - - @Mock lateinit var sinkProvider: TransitionProgressListener - lateinit var progressProvider: ScaleAwareTransitionProgressProvider + private val sourceProvider = TestUnfoldTransitionProvider() - private val sourceProviderListenerCaptor = - ArgumentCaptor.forClass(TransitionProgressListener::class.java) + lateinit var progressProvider: ScaleAwareTransitionProgressProvider private val animatorDurationScaleListenerCaptor = ArgumentCaptor.forClass(ContentObserver::class.java) @@ -63,7 +60,6 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { contentResolver ) - verify(sourceProvider).addCallback(sourceProviderListenerCaptor.capture()) verify(contentResolver).registerContentObserver(any(), any(), animatorDurationScaleListenerCaptor.capture()) @@ -74,7 +70,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionStarted_animationsEnabled_eventReceived() { setAnimationsEnabled(true) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() verify(sinkProvider).onTransitionStarted() } @@ -83,7 +79,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionStarted_animationsNotEnabled_eventNotReceived() { setAnimationsEnabled(false) - source.onTransitionStarted() + sourceProvider.onTransitionStarted() verifyNoMoreInteractions(sinkProvider) } @@ -92,7 +88,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionEnd_animationsEnabled_eventReceived() { setAnimationsEnabled(true) - source.onTransitionFinished() + sourceProvider.onTransitionFinished() verify(sinkProvider).onTransitionFinished() } @@ -101,7 +97,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionEnd_animationsNotEnabled_eventNotReceived() { setAnimationsEnabled(false) - source.onTransitionFinished() + sourceProvider.onTransitionFinished() verifyNoMoreInteractions(sinkProvider) } @@ -110,7 +106,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionProgress_animationsEnabled_eventReceived() { setAnimationsEnabled(true) - source.onTransitionProgress(42f) + sourceProvider.onTransitionProgress(42f) verify(sinkProvider).onTransitionProgress(42f) } @@ -119,7 +115,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { fun onTransitionProgress_animationsNotEnabled_eventNotReceived() { setAnimationsEnabled(false) - source.onTransitionProgress(42f) + sourceProvider.onTransitionProgress(42f) verifyNoMoreInteractions(sinkProvider) } @@ -133,7 +129,4 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { ValueAnimator.setDurationScale(durationScale) animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false) } - - private val source: TransitionProgressListener - get() = sourceProviderListenerCaptor.value } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt index 8f851ec60981..a064e8c81076 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt @@ -24,6 +24,8 @@ import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener class TestFoldStateProvider : FoldStateProvider { private val listeners: MutableList<FoldUpdatesListener> = arrayListOf() + val hasListeners: Boolean + get() = listeners.isNotEmpty() override fun start() { } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/NotificationChannelsTest.java index c7bcdefdc4c3..900d7928fb44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/ChannelsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/NotificationChannelsTest.java @@ -28,7 +28,6 @@ import android.util.ArraySet; import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; -import com.android.systemui.util.NotificationChannels; import org.junit.Before; import org.junit.Test; @@ -41,7 +40,7 @@ import java.util.Set; @SmallTest @RunWith(AndroidJUnit4.class) -public class ChannelsTest extends SysuiTestCase { +public class NotificationChannelsTest extends SysuiTestCase { private final NotificationManager mMockNotificationManager = mock(NotificationManager.class); @Before @@ -55,9 +54,10 @@ public class ChannelsTest extends SysuiTestCase { NotificationChannels.ALERTS, NotificationChannels.SCREENSHOTS_HEADSUP, NotificationChannels.STORAGE, - NotificationChannels.GENERAL, + NotificationChannels.INSTANT, NotificationChannels.BATTERY, - NotificationChannels.HINTS + NotificationChannels.HINTS, + NotificationChannels.SETUP )); NotificationChannels.createAll(mContext); ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class); @@ -67,4 +67,11 @@ public class ChannelsTest extends SysuiTestCase { list.forEach((chan) -> assertTrue(ALL_CHANNELS.contains(chan.getId()))); } + @Test + public void testChannelCleanup() { + new NotificationChannels(mContext).start(); + ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class); + verify(mMockNotificationManager).deleteNotificationChannel(captor.capture()); + assertEquals(NotificationChannels.GENERAL, captor.getValue()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java index 5118637ea710..758961658f2a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionMonitorTest.java @@ -65,7 +65,7 @@ public class ConditionMonitorTest extends SysuiTestCase { mCondition3 = spy(new FakeCondition()); mConditions = new HashSet<>(Arrays.asList(mCondition1, mCondition2, mCondition3)); - mConditionMonitor = new Monitor(mExecutor, mConditions, null /*callbacks*/); + mConditionMonitor = new Monitor(mExecutor, mConditions); } @Test @@ -76,8 +76,10 @@ public class ConditionMonitorTest extends SysuiTestCase { final Monitor monitor = new Monitor( mExecutor, - new HashSet<>(Arrays.asList(overridingCondition, regularCondition)), - new HashSet<>(Arrays.asList(callback))); + new HashSet<>(Arrays.asList(overridingCondition, regularCondition))); + + monitor.addCallback(callback); + mExecutor.runAllReady(); when(overridingCondition.isOverridingCondition()).thenReturn(true); when(overridingCondition.isConditionMet()).thenReturn(true); @@ -123,8 +125,9 @@ public class ConditionMonitorTest extends SysuiTestCase { final Monitor monitor = new Monitor( mExecutor, new HashSet<>(Arrays.asList(overridingCondition, overridingCondition2, - regularCondition)), - new HashSet<>(Arrays.asList(callback))); + regularCondition))); + monitor.addCallback(callback); + mExecutor.runAllReady(); when(overridingCondition.isOverridingCondition()).thenReturn(true); when(overridingCondition.isConditionMet()).thenReturn(true); @@ -174,8 +177,8 @@ public class ConditionMonitorTest extends SysuiTestCase { mock(Monitor.Callback.class); final Condition condition = mock(Condition.class); when(condition.isConditionMet()).thenReturn(true); - final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)), - new HashSet<>(Arrays.asList(callback1))); + final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition))); + monitor.addCallback(callback1); final Monitor.Callback callback2 = mock(Monitor.Callback.class); @@ -186,7 +189,7 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void addCallback_noConditions_reportAllConditionsMet() { - final Monitor monitor = new Monitor(mExecutor, new HashSet<>(), null /*callbacks*/); + final Monitor monitor = new Monitor(mExecutor, new HashSet<>()); final Monitor.Callback callback = mock(Monitor.Callback.class); monitor.addCallback(callback); @@ -196,7 +199,7 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void addCallback_withMultipleInstancesOfTheSameCallback_registerOnlyOne() { - final Monitor monitor = new Monitor(mExecutor, new HashSet<>(), null /*callbacks*/); + final Monitor monitor = new Monitor(mExecutor, new HashSet<>()); final Monitor.Callback callback = mock(Monitor.Callback.class); // Adds the same instance multiple times. @@ -212,8 +215,7 @@ public class ConditionMonitorTest extends SysuiTestCase { @Test public void removeCallback_shouldNoLongerReceiveUpdate() { final Condition condition = mock(Condition.class); - final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition)), - null); + final Monitor monitor = new Monitor(mExecutor, new HashSet<>(Arrays.asList(condition))); final Monitor.Callback callback = mock(Monitor.Callback.class); monitor.addCallback(callback); diff --git a/packages/SystemUI/unfold/Android.bp b/packages/SystemUI/unfold/Android.bp new file mode 100644 index 000000000000..108295b90e58 --- /dev/null +++ b/packages/SystemUI/unfold/Android.bp @@ -0,0 +1,39 @@ +// Copyright (C) 2022 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], +} + +android_library { + name: "SystemUIUnfoldLib", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + "src/**/*.aidl", + ], + static_libs: [ + "androidx.dynamicanimation_dynamicanimation", + "dagger2", + "jsr330", + ], + java_version: "1.8", + min_sdk_version: "current", + plugins: ["dagger2-compiler"], +} diff --git a/libs/WindowManager/Shell/res/color/unfold_transition_background.xml b/packages/SystemUI/unfold/AndroidManifest.xml index 63289a3f75d9..ee8afe1aff5b 100644 --- a/libs/WindowManager/Shell/res/color/unfold_transition_background.xml +++ b/packages/SystemUI/unfold/AndroidManifest.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2021 The Android Open Source Project +<!-- + Copyright (C) 2017 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. @@ -13,7 +14,9 @@ See the License for the specific language governing permissions and limitations under the License. --> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <!-- Matches taskbar color --> - <item android:color="@android:color/system_neutral2_500" android:lStar="35" /> -</selector> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.systemui.unfold"> + + +</manifest> diff --git a/packages/SystemUI/unfold/lint-baseline.xml b/packages/SystemUI/unfold/lint-baseline.xml new file mode 100644 index 000000000000..449ed2e60853 --- /dev/null +++ b/packages/SystemUI/unfold/lint-baseline.xml @@ -0,0 +1,3 @@ +<?xml version="1.0" encoding="UTF-8"?> +<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" name="" variant="all" version="7.1.0-dev"> +</issues> diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt index 9e5aeb84b624..a5ec0a454412 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedComponent.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedComponent.kt @@ -16,16 +16,16 @@ package com.android.systemui.unfold -import android.app.ActivityManager import android.content.ContentResolver import android.content.Context import android.hardware.SensorManager -import android.hardware.devicestate.DeviceStateManager import android.os.Handler -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.dagger.UnfoldBackground +import com.android.systemui.unfold.dagger.UnfoldMain +import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.util.CurrentActivityTypeProvider import com.android.systemui.unfold.util.UnfoldTransitionATracePrefix import dagger.BindsInstance import dagger.Component @@ -51,12 +51,12 @@ internal interface UnfoldSharedComponent { @BindsInstance context: Context, @BindsInstance config: UnfoldTransitionConfig, @BindsInstance screenStatusProvider: ScreenStatusProvider, - @BindsInstance deviceStateManager: DeviceStateManager, - @BindsInstance activityManager: ActivityManager, + @BindsInstance foldProvider: FoldProvider, + @BindsInstance activityTypeProvider: CurrentActivityTypeProvider, @BindsInstance sensorManager: SensorManager, - @BindsInstance @Main handler: Handler, - @BindsInstance @Main executor: Executor, - @BindsInstance @UiBackground backgroundExecutor: Executor, + @BindsInstance @UnfoldMain handler: Handler, + @BindsInstance @UnfoldMain executor: Executor, + @BindsInstance @UnfoldBackground backgroundExecutor: Executor, @BindsInstance @UnfoldTransitionATracePrefix tracingTagPrefix: String, @BindsInstance contentResolver: ContentResolver = context.contentResolver ): UnfoldSharedComponent diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt index c612995241ef..8f4ee4dc9838 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldSharedModule.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt @@ -17,8 +17,8 @@ package com.android.systemui.unfold import android.hardware.SensorManager -import com.android.systemui.dagger.qualifiers.UiBackground import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.dagger.UnfoldBackground import com.android.systemui.unfold.progress.FixedTimingTransitionProgressProvider import com.android.systemui.unfold.progress.PhysicsBasedUnfoldTransitionProgressProvider import com.android.systemui.unfold.updates.DeviceFoldStateProvider @@ -70,7 +70,7 @@ class UnfoldSharedModule { fun hingeAngleProvider( config: UnfoldTransitionConfig, sensorManager: SensorManager, - @UiBackground executor: Executor + @UnfoldBackground executor: Executor ): HingeAngleProvider = if (config.isHingeAngleEnabled) { HingeSensorAngleProvider(sensorManager, executor) diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt index cc56007c431a..402dd8474bc4 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt @@ -17,14 +17,13 @@ package com.android.systemui.unfold -import android.app.ActivityManager import android.content.Context import android.hardware.SensorManager -import android.hardware.devicestate.DeviceStateManager import android.os.Handler -import com.android.systemui.unfold.config.ResourceUnfoldTransitionConfig import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.util.CurrentActivityTypeProvider import java.util.concurrent.Executor /** @@ -39,8 +38,8 @@ fun createUnfoldTransitionProgressProvider( context: Context, config: UnfoldTransitionConfig, screenStatusProvider: ScreenStatusProvider, - deviceStateManager: DeviceStateManager, - activityManager: ActivityManager, + foldProvider: FoldProvider, + activityTypeProvider: CurrentActivityTypeProvider, sensorManager: SensorManager, mainHandler: Handler, mainExecutor: Executor, @@ -52,8 +51,8 @@ fun createUnfoldTransitionProgressProvider( context, config, screenStatusProvider, - deviceStateManager, - activityManager, + foldProvider, + activityTypeProvider, sensorManager, mainHandler, mainExecutor, @@ -64,5 +63,3 @@ fun createUnfoldTransitionProgressProvider( ?: throw IllegalStateException( "Trying to create " + "UnfoldTransitionProgressProvider when the transition is disabled") - -fun createConfig(context: Context): UnfoldTransitionConfig = ResourceUnfoldTransitionConfig(context) diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt index 409dc95ab131..d54481c72bfd 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt @@ -15,9 +15,9 @@ */ package com.android.systemui.unfold -import android.annotation.FloatRange -import com.android.systemui.statusbar.policy.CallbackController +import androidx.annotation.FloatRange import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener +import com.android.systemui.unfold.util.CallbackController /** * Interface that allows to receive unfold transition progress updates. diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt new file mode 100644 index 000000000000..2044f05664d0 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 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.unfold.compat + +import android.content.Context +import android.content.res.Configuration +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.updates.FoldProvider.FoldCallback +import java.util.concurrent.Executor + +/** + * Fold provider that notifies about fold state based on the screen size + * It could be used when no activity context is available + * TODO(b/232369816): use Jetpack WM library when non-activity contexts supported b/169740873 + */ +class ScreenSizeFoldProvider(private val context: Context) : FoldProvider { + + private var callbacks: MutableList<FoldCallback> = arrayListOf() + private var lastWidth: Int = 0 + + override fun registerCallback(callback: FoldCallback, executor: Executor) { + callbacks += callback + onConfigurationChange(context.resources.configuration) + } + + override fun unregisterCallback(callback: FoldCallback) { + callbacks -= callback + } + + fun onConfigurationChange(newConfig: Configuration) { + if (lastWidth == newConfig.smallestScreenWidthDp) { + return + } + + if (newConfig.smallestScreenWidthDp > INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP) { + callbacks.forEach { it.onFoldUpdated(false) } + } else { + callbacks.forEach { it.onFoldUpdated(true) } + } + lastWidth = newConfig.smallestScreenWidthDp + } +} + +private const val INNER_SCREEN_SMALLEST_SCREEN_WIDTH_THRESHOLD_DP = 600 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt new file mode 100644 index 000000000000..c405f3110297 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 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.unfold.compat + +import com.android.systemui.unfold.updates.FoldProvider +import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener +import java.util.concurrent.Executor + +class SizeScreenStatusProvider( + private val foldProvider: FoldProvider, + private val executor: Executor +) : ScreenStatusProvider { + + private val listeners: MutableList<ScreenListener> = arrayListOf() + private val callback = object : FoldProvider.FoldCallback { + override fun onFoldUpdated(isFolded: Boolean) { + if (!isFolded) { + listeners.forEach { it.onScreenTurnedOn() } + } + } + } + + fun start() { + foldProvider.registerCallback( + callback, + executor + ) + } + + fun stop() { + foldProvider.unregisterCallback(callback) + } + + override fun addCallback(listener: ScreenListener) { + listeners.add(listener) + } + + override fun removeCallback(listener: ScreenListener) { + listeners.remove(listener) + } +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt new file mode 100644 index 000000000000..c51372975a67 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 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.unfold.config + +import android.content.res.Resources +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ResourceUnfoldTransitionConfig @Inject constructor() : UnfoldTransitionConfig { + + override val isEnabled: Boolean by lazy { + val id = Resources.getSystem() + .getIdentifier("config_unfoldTransitionEnabled", "bool", "android") + Resources.getSystem().getBoolean(id) + } + + override val isHingeAngleEnabled: Boolean by lazy { + val id = Resources.getSystem() + .getIdentifier("config_unfoldTransitionHingeAngle", "bool", "android") + Resources.getSystem().getBoolean(id) + } + + override val halfFoldedTimeoutMillis: Int by lazy { + val id = Resources.getSystem() + .getIdentifier("config_unfoldTransitionHalfFoldedTimeout", "integer", "android") + Resources.getSystem().getInteger(id) + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt index 5b187b3486c6..765e862aa00d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/config/UnfoldTransitionConfig.kt @@ -18,4 +18,5 @@ package com.android.systemui.unfold.config interface UnfoldTransitionConfig { val isEnabled: Boolean val isHingeAngleEnabled: Boolean + val halfFoldedTimeoutMillis: Int } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt new file mode 100644 index 000000000000..60747954dac3 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 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.unfold.dagger + +import javax.inject.Qualifier + +/** + * Alternative to [UiBackground] qualifier annotation in unfold module. + * It is needed as we can't depend on SystemUI code in this module. + */ +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class UnfoldBackground diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt new file mode 100644 index 000000000000..5553690fb562 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 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.unfold.dagger + +import javax.inject.Qualifier + +/** + * Alternative to [Main] qualifier annotation in unfold module. + * It is needed as we can't depend on SystemUI code in this module. + */ +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +annotation class UnfoldMain diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt index 4c85b055aeae..4c85b055aeae 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt index 04d920cb15d5..2ab28c65f32f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt @@ -16,7 +16,6 @@ package com.android.systemui.unfold.progress import android.util.Log -import android.util.MathUtils.saturate import androidx.dynamicanimation.animation.DynamicAnimation import androidx.dynamicanimation.animation.FloatPropertyCompat import androidx.dynamicanimation.animation.SpringAnimation @@ -70,6 +69,9 @@ class PhysicsBasedUnfoldTransitionProgressProvider( springAnimation.animateToFinalPosition(progress) } + private fun saturate(amount: Float, low: Float = 0f, high: Float = 1f): Float = + if (amount < low) low else if (amount > high) high else amount + override fun onFoldUpdate(@FoldUpdate update: Int) { when (update) { FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> { diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt index 14581ccd5c0a..e8038fd7dfa6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt @@ -15,46 +15,46 @@ */ package com.android.systemui.unfold.updates -import android.annotation.FloatRange -import android.app.ActivityManager -import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME -import android.content.Context -import android.hardware.devicestate.DeviceStateManager import android.os.Handler import android.util.Log +import androidx.annotation.FloatRange import androidx.annotation.VisibleForTesting import androidx.core.util.Consumer -import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.unfold.config.UnfoldTransitionConfig +import com.android.systemui.unfold.dagger.UnfoldMain import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener import com.android.systemui.unfold.updates.hinge.FULLY_CLOSED_DEGREES import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES import com.android.systemui.unfold.updates.hinge.HingeAngleProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider +import com.android.systemui.unfold.util.CurrentActivityTypeProvider import java.util.concurrent.Executor import javax.inject.Inject class DeviceFoldStateProvider @Inject constructor( - context: Context, + config: UnfoldTransitionConfig, private val hingeAngleProvider: HingeAngleProvider, private val screenStatusProvider: ScreenStatusProvider, - private val deviceStateManager: DeviceStateManager, - private val activityManager: ActivityManager, - @Main private val mainExecutor: Executor, - @Main private val handler: Handler + private val foldProvider: FoldProvider, + private val activityTypeProvider: CurrentActivityTypeProvider, + @UnfoldMain private val mainExecutor: Executor, + @UnfoldMain private val handler: Handler ) : FoldStateProvider { private val outputListeners: MutableList<FoldUpdatesListener> = mutableListOf() - @FoldUpdate private var lastFoldUpdate: Int? = null + @FoldUpdate + private var lastFoldUpdate: Int? = null - @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f + @FloatRange(from = 0.0, to = 180.0) + private var lastHingeAngle: Float = 0f private val hingeAngleListener = HingeAngleListener() private val screenListener = ScreenStatusListener() - private val foldStateListener = FoldStateListener(context) + private val foldStateListener = FoldStateListener() private val timeoutRunnable = TimeoutRunnable() /** @@ -62,22 +62,20 @@ constructor( * [FOLD_UPDATE_START_CLOSING] or [FOLD_UPDATE_START_OPENING] event, if an end state is not * reached. */ - private val halfOpenedTimeoutMillis: Int = - context.resources.getInteger( - com.android.internal.R.integer.config_unfoldTransitionHalfFoldedTimeout) + private val halfOpenedTimeoutMillis: Int = config.halfFoldedTimeoutMillis private var isFolded = false private var isUnfoldHandled = true override fun start() { - deviceStateManager.registerCallback(mainExecutor, foldStateListener) + foldProvider.registerCallback(foldStateListener, mainExecutor) screenStatusProvider.addCallback(screenListener) hingeAngleProvider.addCallback(hingeAngleListener) } override fun stop() { screenStatusProvider.removeCallback(screenListener) - deviceStateManager.unregisterCallback(foldStateListener) + foldProvider.unregisterCallback(foldStateListener) hingeAngleProvider.removeCallback(hingeAngleListener) hingeAngleProvider.stop() } @@ -92,13 +90,13 @@ constructor( override val isFinishedOpening: Boolean get() = !isFolded && - (lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN || - lastFoldUpdate == FOLD_UPDATE_FINISH_HALF_OPEN) + (lastFoldUpdate == FOLD_UPDATE_FINISH_FULL_OPEN || + lastFoldUpdate == FOLD_UPDATE_FINISH_HALF_OPEN) private val isTransitionInProgress: Boolean get() = lastFoldUpdate == FOLD_UPDATE_START_OPENING || - lastFoldUpdate == FOLD_UPDATE_START_CLOSING + lastFoldUpdate == FOLD_UPDATE_START_CLOSING private fun onHingeAngle(angle: Float) { if (DEBUG) { @@ -136,39 +134,36 @@ constructor( * apps that support table-top/HALF_FOLDED mode. Only for launcher, there is no threshold. */ private fun getClosingThreshold(): Int? { - val activityType = - activityManager.getRunningTasks(/* maxNum= */ 1)?.getOrNull(0)?.topActivityType - ?: return null + val isHomeActivity = activityTypeProvider.isHomeActivity ?: return null if (DEBUG) { - Log.d(TAG, "activityType=" + activityType) + Log.d(TAG, "isHomeActivity=$isHomeActivity") } - return if (activityType == ACTIVITY_TYPE_HOME) { + return if (isHomeActivity) { null } else { START_CLOSING_ON_APPS_THRESHOLD_DEGREES } } - private inner class FoldStateListener(context: Context) : - DeviceStateManager.FoldStateListener( - context, - { folded: Boolean -> - isFolded = folded - lastHingeAngle = FULLY_CLOSED_DEGREES - - if (folded) { - hingeAngleProvider.stop() - notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) - cancelTimeout() - isUnfoldHandled = false - } else { - notifyFoldUpdate(FOLD_UPDATE_START_OPENING) - rescheduleAbortAnimationTimeout() - hingeAngleProvider.start() - } - }) + private inner class FoldStateListener : FoldProvider.FoldCallback { + override fun onFoldUpdated(isFolded: Boolean) { + this@DeviceFoldStateProvider.isFolded = isFolded + lastHingeAngle = FULLY_CLOSED_DEGREES + + if (isFolded) { + hingeAngleProvider.stop() + notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) + cancelTimeout() + isUnfoldHandled = false + } else { + notifyFoldUpdate(FOLD_UPDATE_START_OPENING) + rescheduleAbortAnimationTimeout() + hingeAngleProvider.start() + } + } + } private fun notifyFoldUpdate(@FoldUpdate update: Int) { if (DEBUG) { @@ -234,7 +229,9 @@ private const val TAG = "DeviceFoldProvider" private const val DEBUG = false /** Threshold after which we consider the device fully unfolded. */ -@VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f +@VisibleForTesting +const val FULLY_OPEN_THRESHOLD_DEGREES = 15f /** Fold animation on top of apps only when the angle exceeds this threshold. */ -@VisibleForTesting const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60 +@VisibleForTesting +const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt new file mode 100644 index 000000000000..6e87beeb295f --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldProvider.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 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.unfold.updates + +import java.util.concurrent.Executor + +interface FoldProvider { + fun registerCallback(callback: FoldCallback, executor: Executor) + fun unregisterCallback(callback: FoldCallback) + + interface FoldCallback { + fun onFoldUpdated(isFolded: Boolean) + } +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt index 14a3a70fc6b0..c7a8bf336777 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/FoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt @@ -15,10 +15,10 @@ */ package com.android.systemui.unfold.updates -import android.annotation.FloatRange -import android.annotation.IntDef -import com.android.systemui.statusbar.policy.CallbackController +import androidx.annotation.FloatRange +import androidx.annotation.IntDef import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener +import com.android.systemui.unfold.util.CallbackController /** * Allows to subscribe to main events related to fold/unfold process such as hinge angle update, @@ -36,7 +36,6 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> { } @IntDef( - prefix = ["FOLD_UPDATE_"], value = [ FOLD_UPDATE_START_OPENING, diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt new file mode 100644 index 000000000000..e985506bd989 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/EmptyHingeAngleProvider.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2022 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.unfold.updates.hinge + +import androidx.core.util.Consumer + +internal object EmptyHingeAngleProvider : HingeAngleProvider { + override fun start() {} + + override fun stop() {} + + override fun removeCallback(listener: Consumer<Float>) {} + + override fun addCallback(listener: Consumer<Float>) {} +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt new file mode 100644 index 000000000000..e464c3f81546 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeAngleProvider.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 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.unfold.updates.hinge + +import androidx.core.util.Consumer +import com.android.systemui.unfold.util.CallbackController + +/** + * Emits device hinge angle values (angle between two integral parts of the device). + * + * The hinge angle could be from 0 to 360 degrees inclusive. For foldable devices usually 0 + * corresponds to fully closed (folded) state and 180 degrees corresponds to fully open (flat) + * state. + */ +interface HingeAngleProvider : CallbackController<Consumer<Float>> { + fun start() + fun stop() +} + +const val FULLY_OPEN_DEGREES = 180f +const val FULLY_CLOSED_DEGREES = 0f diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt index c93412b53817..3fc5d610dc2d 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2022 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.unfold.updates.hinge import android.hardware.Sensor diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt index 668c69442cac..d95e050474de 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.unfold.updates.screen -import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener +import com.android.systemui.unfold.util.CallbackController interface ScreenStatusProvider : CallbackController<ScreenListener> { diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt index 1574c8d37ab1..d8bc01804f14 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ATraceLoggerTransitionProgressListener.kt @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2022 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.unfold.util import android.os.Trace diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CallbackController.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CallbackController.kt new file mode 100644 index 000000000000..46ad534722cd --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CallbackController.kt @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2022 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.unfold.util + +interface CallbackController<T> { + fun addCallback(listener: T) + fun removeCallback(listener: T) +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt new file mode 100644 index 000000000000..d0e6cdc9a3c6 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/CurrentActivityTypeProvider.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022 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.unfold.util + +interface CurrentActivityTypeProvider { + val isHomeActivity: Boolean? +} + +class EmptyCurrentActivityTypeProvider(override val isHomeActivity: Boolean? = null) : + CurrentActivityTypeProvider diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt index dfe87921dd42..5c92b3499835 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt @@ -1,3 +1,17 @@ +/* + * Copyright (C) 2022 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.unfold.util import android.animation.ValueAnimator diff --git a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt index 8491f832b740..8491f832b740 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto index dfa34bb50805..0a4ecb227548 100644 --- a/proto/src/system_messages.proto +++ b/proto/src/system_messages.proto @@ -286,10 +286,18 @@ message SystemMessage { // Package: android NOTE_MTE_OVERRIDE_ENABLED = 69; + // Notify the user that this is a guest session with information + // about first login and ephemeral state + // Package: android + NOTE_GUEST_SESSION = 70; + // Inform the user of notification permissions changes. // Package: android NOTE_REVIEW_NOTIFICATION_PERMISSIONS = 71; + // Notify the user to setup their dock + NOTE_SETUP_DOCK = 72; + // ADD_NEW_IDS_ABOVE_THIS_LINE // Legacy IDs with arbitrary values appear below // Legacy IDs existed as stable non-conflicting constants prior to the O release diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index b059cc7e2aa2..7d8c19f41ae4 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -168,8 +168,8 @@ public class AccountManagerService } @Override - public void onUserStopping(@NonNull TargetUser user) { - Slog.i(TAG, "onStopUser " + user); + public void onUserStopped(@NonNull TargetUser user) { + Slog.i(TAG, "onUserStopped " + user); mService.purgeUserData(user.getUserIdentifier()); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 71682f26e664..f5347055f104 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2297,13 +2297,9 @@ public class ActivityManagerService extends IActivityManager.Stub return mAppOpsManager; } - /** - * Provides the basic functionality for activity task related tests when a handler thread is - * given to initialize the dependency members. - */ + /** Provides the basic functionality for unit tests. */ @VisibleForTesting - public ActivityManagerService(Injector injector, ServiceThread handlerThread) { - final boolean hasHandlerThread = handlerThread != null; + ActivityManagerService(Injector injector, @NonNull ServiceThread handlerThread) { mInjector = injector; mContext = mInjector.getContext(); mUiContext = null; @@ -2311,33 +2307,27 @@ public class ActivityManagerService extends IActivityManager.Stub mPackageWatchdog = null; mAppOpsService = mInjector.getAppOpsService(null /* file */, null /* handler */); mBatteryStatsService = null; - mHandler = hasHandlerThread ? new MainHandler(handlerThread.getLooper()) : null; + mHandler = new MainHandler(handlerThread.getLooper()); mHandlerThread = handlerThread; - mConstants = hasHandlerThread - ? new ActivityManagerConstants(mContext, this, mHandler) : null; + mConstants = new ActivityManagerConstants(mContext, this, mHandler); final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */); mPlatformCompat = null; mProcessList = injector.getProcessList(this); mProcessList.init(this, activeUids, mPlatformCompat); mAppProfiler = new AppProfiler(this, BackgroundThread.getHandler().getLooper(), null); mPhantomProcessList = new PhantomProcessList(this); - mOomAdjuster = hasHandlerThread - ? new OomAdjuster(this, mProcessList, activeUids, handlerThread) : null; + mOomAdjuster = new OomAdjuster(this, mProcessList, activeUids, handlerThread); - mIntentFirewall = hasHandlerThread - ? new IntentFirewall(new IntentFirewallInterface(), mHandler) : null; + mIntentFirewall = null; mProcessStats = null; mCpHelper = new ContentProviderHelper(this, false); - // For the usage of {@link ActiveServices#cleanUpServices} that may be invoked from - // {@link ActivityTaskSupervisor#cleanUpRemovedTaskLocked}. - mServices = hasHandlerThread ? new ActiveServices(this) : null; + mServices = null; mSystemThread = null; mUiHandler = injector.getUiHandler(null /* service */); mUidObserverController = new UidObserverController(mUiHandler); - mUserController = hasHandlerThread ? new UserController(this) : null; - mPendingIntentController = hasHandlerThread - ? new PendingIntentController(handlerThread.getLooper(), mUserController, - mConstants) : null; + mUserController = new UserController(this); + mPendingIntentController = + new PendingIntentController(handlerThread.getLooper(), mUserController, mConstants); mAppRestrictionController = new AppRestrictionController(mContext, this); mProcStartHandlerThread = null; mProcStartHandler = null; @@ -2346,7 +2336,7 @@ public class ActivityManagerService extends IActivityManager.Stub mFactoryTest = FACTORY_TEST_OFF; mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class); mInternal = new LocalService(); - mPendingStartActivityUids = new PendingStartActivityUids(mContext); + mPendingStartActivityUids = new PendingStartActivityUids(); mUseFifoUiScheduling = false; mEnableOffloadQueue = false; mFgBroadcastQueue = mBgBroadcastQueue = mBgOffloadBroadcastQueue = @@ -2482,7 +2472,7 @@ public class ActivityManagerService extends IActivityManager.Stub } mInternal = new LocalService(); - mPendingStartActivityUids = new PendingStartActivityUids(mContext); + mPendingStartActivityUids = new PendingStartActivityUids(); mTraceErrorLogger = new TraceErrorLogger(); mComponentAliasResolver = new ComponentAliasResolver(this); } @@ -17373,7 +17363,7 @@ public class ActivityManagerService extends IActivityManager.Stub // next top activity on time. This race will fail the following binder transactions WM // sends to the activity. After this race issue between WM/ATMS and AMS is solved, this // workaround can be removed. (b/213288355) - if (isNewPending && mOomAdjuster != null) { // It can be null in unit test. + if (isNewPending) { mOomAdjuster.mCachedAppOptimizer.unfreezeProcess(pid); } // We need to update the network rules for the app coming to the top state so that diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index e7fcc5989467..a61cbbfbfc91 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1554,14 +1554,22 @@ public class OomAdjuster { boolean foregroundActivities = false; boolean hasVisibleActivities = false; - if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP && app == topApp) { + if (app == topApp && (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP + || PROCESS_STATE_CUR_TOP == PROCESS_STATE_IMPORTANT_FOREGROUND)) { // The last app on the list is the foreground app. adj = ProcessList.FOREGROUND_APP_ADJ; - schedGroup = ProcessList.SCHED_GROUP_TOP_APP; - state.setAdjType("top-activity"); + if (PROCESS_STATE_CUR_TOP == PROCESS_STATE_TOP) { + schedGroup = ProcessList.SCHED_GROUP_TOP_APP; + state.setAdjType("top-activity"); + } else { + // Demote the scheduling group to avoid CPU contention if there is another more + // important process which also uses top-app, such as if SystemUI is animating. + schedGroup = ProcessList.SCHED_GROUP_DEFAULT; + state.setAdjType("intermediate-top-activity"); + } foregroundActivities = true; hasVisibleActivities = true; - procState = PROCESS_STATE_CUR_TOP; + procState = PROCESS_STATE_TOP; if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Making top: " + app); } diff --git a/services/core/java/com/android/server/am/PendingStartActivityUids.java b/services/core/java/com/android/server/am/PendingStartActivityUids.java index bd600571f0a3..da09317add90 100644 --- a/services/core/java/com/android/server/am/PendingStartActivityUids.java +++ b/services/core/java/com/android/server/am/PendingStartActivityUids.java @@ -16,7 +16,6 @@ package com.android.server.am; -import android.content.Context; import android.os.SystemClock; import android.util.Pair; import android.util.Slog; @@ -40,11 +39,6 @@ final class PendingStartActivityUids { // Key is uid, value is Pair of pid and SystemClock.elapsedRealtime() when the // uid is added. private final SparseArray<Pair<Integer, Long>> mPendingUids = new SparseArray(); - private Context mContext; - - PendingStartActivityUids(Context context) { - mContext = context; - } /** Returns {@code true} if the uid is put to the pending array. Otherwise it existed. */ synchronized boolean add(int uid, int pid) { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index e6bc79679a8f..5a40b30551b6 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -1447,6 +1447,16 @@ abstract public class ManagedServices { } } + @VisibleForTesting + void reregisterService(final ComponentName cn, final int userId) { + // If rebinding a package that died, ensure it still has permission + // after the rebind delay + if (isPackageOrComponentAllowed(cn.getPackageName(), userId) + || isPackageOrComponentAllowed(cn.flattenToString(), userId)) { + registerService(cn, userId); + } + } + /** * Inject a system service into the management list. */ @@ -1545,12 +1555,9 @@ abstract public class ManagedServices { unbindService(this, name, userid); if (!mServicesRebinding.contains(servicesBindingTag)) { mServicesRebinding.add(servicesBindingTag); - mHandler.postDelayed(new Runnable() { - @Override - public void run() { - registerService(name, userid); - } - }, ON_BINDING_DIED_REBIND_DELAY_MS); + mHandler.postDelayed(() -> + reregisterService(name, userid), + ON_BINDING_DIED_REBIND_DELAY_MS); } else { Slog.v(TAG, getCaption() + " not rebinding in user " + userid + " as a previous rebind attempt was made: " + name); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d1e0b0474b61..fdb31b2e9bd8 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -517,7 +517,7 @@ public class NotificationManagerService extends SystemService { private ActivityManagerInternal mAmi; private IPackageManager mPackageManager; private PackageManager mPackageManagerClient; - private PackageManagerInternal mPackageManagerInternal; + PackageManagerInternal mPackageManagerInternal; private PermissionPolicyInternal mPermissionPolicyInternal; AudioManager mAudioManager; AudioManagerInternal mAudioManagerInternal; @@ -9798,7 +9798,7 @@ public class NotificationManagerService extends SystemService { * notifications visible to the given listener. */ @GuardedBy("mNotificationLock") - private NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { + NotificationRankingUpdate makeRankingUpdateLocked(ManagedServiceInfo info) { final int N = mNotificationList.size(); final ArrayList<NotificationListenerService.Ranking> rankings = new ArrayList<>(); @@ -10913,7 +10913,7 @@ public class NotificationManagerService extends SystemService { TrimCache trimCache = new TrimCache(sbn); for (final ManagedServiceInfo info : getServices()) { - boolean sbnVisible = isVisibleToListener(sbn, r. getNotificationType(), info); + boolean sbnVisible = isVisibleToListener(sbn, r.getNotificationType(), info); boolean oldSbnVisible = (oldSbn != null) && isVisibleToListener(oldSbn, old.getNotificationType(), info); // This notification hasn't been and still isn't visible -> ignore. @@ -10943,12 +10943,17 @@ public class NotificationManagerService extends SystemService { info, oldSbnLightClone, update, null, REASON_USER_STOPPED)); continue; } - // Grant access before listener is notified final int targetUserId = (info.userid == UserHandle.USER_ALL) ? UserHandle.USER_SYSTEM : info.userid; updateUriPermissions(r, old, info.component.getPackageName(), targetUserId); + mPackageManagerInternal.grantImplicitAccess( + targetUserId, null /* intent */, + UserHandle.getAppId(info.uid), + sbn.getUid(), + false /* direct */, false /* retainOnUpdate */); + final StatusBarNotification sbnToPost = trimCache.ForListener(info); mHandler.post(() -> notifyPosted(info, sbnToPost, update)); } diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 9e0c97502c4f..de9102a69a2e 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -121,6 +121,7 @@ public class ZenModeHelper { protected final RingerModeDelegate mRingerModeDelegate = new RingerModeDelegate(); @VisibleForTesting protected final ZenModeConditions mConditions; + Object mConfigsLock = new Object(); @VisibleForTesting final SparseArray<ZenModeConfig> mConfigs = new SparseArray<>(); private final Metrics mMetrics = new Metrics(); private final ConditionProviders.Config mServiceConfig; @@ -153,7 +154,9 @@ public class ZenModeHelper { mDefaultConfig = readDefaultConfig(mContext.getResources()); updateDefaultAutomaticRuleNames(); mConfig = mDefaultConfig.copy(); - mConfigs.put(UserHandle.USER_SYSTEM, mConfig); + synchronized (mConfigsLock) { + mConfigs.put(UserHandle.USER_SYSTEM, mConfig); + } mConsolidatedPolicy = mConfig.toNotificationPolicy(); mSettingsObserver = new SettingsObserver(mHandler); @@ -233,7 +236,9 @@ public class ZenModeHelper { public void onUserRemoved(int user) { if (user < UserHandle.USER_SYSTEM) return; if (DEBUG) Log.d(TAG, "onUserRemoved u=" + user); - mConfigs.remove(user); + synchronized (mConfigsLock) { + mConfigs.remove(user); + } } public void onUserUnlocked(int user) { @@ -248,7 +253,12 @@ public class ZenModeHelper { if (mUser == user || user < UserHandle.USER_SYSTEM) return; mUser = user; if (DEBUG) Log.d(TAG, reason + " u=" + user); - ZenModeConfig config = mConfigs.get(user); + ZenModeConfig config = null; + synchronized (mConfigsLock) { + if (mConfigs.get(user) != null) { + config = mConfigs.get(user).copy(); + } + } if (config == null) { if (DEBUG) Log.d(TAG, reason + " generating default config for user " + user); config = mDefaultConfig.copy(); @@ -685,9 +695,11 @@ public class ZenModeHelper { pw.println(Global.zenModeToString(mZenMode)); pw.print(prefix); pw.println("mConsolidatedPolicy=" + mConsolidatedPolicy.toString()); - final int N = mConfigs.size(); - for (int i = 0; i < N; i++) { - dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i)); + synchronized(mConfigsLock) { + final int N = mConfigs.size(); + for (int i = 0; i < N; i++) { + dump(pw, prefix, "mConfigs[u=" + mConfigs.keyAt(i) + "]", mConfigs.valueAt(i)); + } } pw.print(prefix); pw.print("mUser="); pw.println(mUser); synchronized (mConfig) { @@ -787,7 +799,7 @@ public class ZenModeHelper { public void writeXml(TypedXmlSerializer out, boolean forBackup, Integer version, int userId) throws IOException { - synchronized (mConfigs) { + synchronized (mConfigsLock) { final int n = mConfigs.size(); for (int i = 0; i < n; i++) { if (forBackup && mConfigs.keyAt(i) != userId) { @@ -883,14 +895,18 @@ public class ZenModeHelper { } if (config.user != mUser) { // simply store away for background users - mConfigs.put(config.user, config); + synchronized (mConfigsLock) { + mConfigs.put(config.user, config); + } if (DEBUG) Log.d(TAG, "setConfigLocked: store config for user " + config.user); return true; } // handle CPS backed conditions - danger! may modify config mConditions.evaluateConfig(config, null, false /*processSubscriptions*/); - mConfigs.put(config.user, config); + synchronized (mConfigsLock) { + mConfigs.put(config.user, config); + } if (DEBUG) Log.d(TAG, "setConfigLocked reason=" + reason, new Throwable()); ZenLog.traceConfig(reason, mConfig, config); @@ -1211,7 +1227,7 @@ public class ZenModeHelper { * Generate pulled atoms about do not disturb configurations. */ public void pullRules(List<StatsEvent> events) { - synchronized (mConfig) { + synchronized (mConfigsLock) { final int numConfigs = mConfigs.size(); for (int i = 0; i < numConfigs; i++) { final int user = mConfigs.keyAt(i); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 9de485b28479..409ca03f4180 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -1878,6 +1878,44 @@ public class UserManagerService extends IUserManager.Stub { } @Override + public boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) { + checkCreateUsersPermission("update ephemeral user flag"); + UserData userToUpdate = null; + synchronized (mPackagesLock) { + synchronized (mUsersLock) { + final UserData userData = mUsers.get(userId); + if (userData == null) { + Slog.e(LOG_TAG, "User not found for setting ephemeral mode: u" + userId); + return false; + } + boolean isEphemeralUser = (userData.info.flags & UserInfo.FLAG_EPHEMERAL) != 0; + boolean isEphemeralOnCreateUser = + (userData.info.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE) != 0; + // when user is created in ephemeral mode via FLAG_EPHEMERAL + // its state cannot be changed to non ephemeral. + // FLAG_EPHEMERAL_ON_CREATE is used to keep track of this state + if (isEphemeralOnCreateUser && !enableEphemeral) { + Slog.e(LOG_TAG, "Failed to change user state to non-ephemeral for user " + + userId); + return false; + } + if (isEphemeralUser != enableEphemeral) { + if (enableEphemeral) { + userData.info.flags |= UserInfo.FLAG_EPHEMERAL; + } else { + userData.info.flags &= ~UserInfo.FLAG_EPHEMERAL; + } + userToUpdate = userData; + } + } + if (userToUpdate != null) { + writeUserLP(userToUpdate); + } + } + return true; + } + + @Override public void setUserIcon(@UserIdInt int userId, Bitmap bitmap) { try { checkManageUsersPermission("update users"); @@ -3984,6 +4022,10 @@ public class UserManagerService extends IUserManager.Stub { flags &= ~UserInfo.FLAG_EPHEMERAL; } + if ((flags & UserInfo.FLAG_EPHEMERAL) != 0) { + flags |= UserInfo.FLAG_EPHEMERAL_ON_CREATE; + } + userInfo = new UserInfo(userId, name, null, flags, userType); userInfo.serialNumber = mNextSerialNumber++; userInfo.creationTime = getCreationTime(); diff --git a/services/core/java/com/android/server/policy/AppOpsPolicy.java b/services/core/java/com/android/server/policy/AppOpsPolicy.java index a6d148c824c9..ebd9126d1439 100644 --- a/services/core/java/com/android/server/policy/AppOpsPolicy.java +++ b/services/core/java/com/android/server/policy/AppOpsPolicy.java @@ -202,9 +202,8 @@ public final class AppOpsPolicy implements AppOpsManagerInternal.CheckOpsDelegat } private static boolean isHotwordDetectionServiceRequired(PackageManager pm) { - // The HotwordDetectionService APIs aren't ready yet for Auto or TV. - return !(pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) - || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)); + // Usage of the HotwordDetectionService won't be enforced until a later release. + return false; } @Override diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index d8e7fbe8b296..d88949bcc0ec 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -169,7 +169,6 @@ import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.IDisplayFoldListener; -import android.view.IWindowManager; import android.view.InputDevice; import android.view.KeyCharacterMap; import android.view.KeyCharacterMap.FallbackAction; @@ -381,7 +380,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { private final SparseArray<ScreenOnListener> mScreenOnListeners = new SparseArray<>(); Context mContext; - IWindowManager mWindowManager; WindowManagerFuncs mWindowManagerFuncs; WindowManagerInternal mWindowManagerInternal; PowerManager mPowerManager; @@ -1870,10 +1868,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ @Override - public void init(Context context, IWindowManager windowManager, - WindowManagerFuncs windowManagerFuncs) { + public void init(Context context, WindowManagerFuncs windowManagerFuncs) { mContext = context; - mWindowManager = windowManager; mWindowManagerFuncs = windowManagerFuncs; mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); @@ -4534,7 +4530,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { pmWakeReason)) + ")"); } - mActivityTaskManagerInternal.notifyWakingUp(); mDefaultDisplayPolicy.setAwake(true); // Since goToSleep performs these functions synchronously, we must @@ -4802,10 +4797,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (enableScreen) { - try { - mWindowManager.enableScreenIfNeeded(); - } catch (RemoteException unhandled) { - } + mWindowManagerFuncs.enableScreenIfNeeded(); } } @@ -5262,12 +5254,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } void updateRotation(boolean alwaysSendConfiguration) { - try { - // Set orientation on WindowManager. - mWindowManager.updateRotation(alwaysSendConfiguration, false /* forceRelayout */); - } catch (RemoteException e) { - // Ignore - } + mWindowManagerFuncs.updateRotation(alwaysSendConfiguration, false /* forceRelayout */); } /** diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 7ca09ab5154f..e8a3dcd5635f 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -78,7 +78,6 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.IDisplayFoldListener; -import android.view.IWindowManager; import android.view.KeyEvent; import android.view.WindowManager; import android.view.WindowManagerGlobal; @@ -343,6 +342,22 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * @return {@code true} if app transition state is idle on the default display. */ boolean isAppTransitionStateIdle(); + + /** + * Enables the screen if all conditions are met. + */ + void enableScreenIfNeeded(); + + /** + * Updates the current screen rotation based on the current state of the world. + * + * @param alwaysSendConfiguration Flag to force a new configuration to be evaluated. + * This can be used when there are other parameters in + * configuration that are changing. + * @param forceRelayout If true, the window manager will always do a relayout of its + * windows even if the rotation hasn't changed. + */ + void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout); } /** @@ -391,8 +406,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * * @param context The system context we are running in. */ - public void init(Context context, IWindowManager windowManager, - WindowManagerFuncs windowManagerFuncs); + void init(Context context, WindowManagerFuncs windowManagerFuncs); /** * Check permissions when adding a window. diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 3c13abf4403c..c56369e41b23 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -110,6 +110,7 @@ import com.android.internal.app.IBatteryStats; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; +import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.LatencyTracker; import com.android.internal.util.Preconditions; import com.android.server.EventLogTags; @@ -4951,6 +4952,7 @@ public final class PowerManagerService extends SystemService int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED); if (mDockState != dockState) { + FrameworkStatsLog.write(FrameworkStatsLog.DOCK_STATE_CHANGED, dockState); mDockState = dockState; mDirty |= DIRTY_DOCK_STATE; updatePowerStateLocked(); diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java index 13fe14caa1f9..6318a99fc9a2 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewarePermission.java @@ -21,6 +21,7 @@ import static android.Manifest.permission.RECORD_AUDIO; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.AppOpsManager; import android.content.Context; import android.content.PermissionChecker; import android.media.permission.Identity; @@ -115,8 +116,10 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware * originator temporarily doesn't have the right permissions to use this service. */ private void enforcePermissionsForPreflight(@NonNull Identity identity) { - enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO); - enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD); + enforcePermissionForPreflight(mContext, identity, RECORD_AUDIO, + /* allowSoftDenial= */ true); + enforcePermissionForPreflight(mContext, identity, CAPTURE_AUDIO_HOTWORD, + /* allowSoftDenial= */ true); } /** @@ -124,8 +127,7 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware */ void enforcePermissionsForDataDelivery(@NonNull Identity identity, @NonNull String reason) { enforceSoundTriggerRecordAudioPermissionForDataDelivery(identity, reason); - enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD, - reason); + enforcePermissionForDataDelivery(mContext, identity, CAPTURE_AUDIO_HOTWORD, reason); } /** @@ -165,20 +167,25 @@ public class SoundTriggerMiddlewarePermission implements ISoundTriggerMiddleware /** * Throws a {@link SecurityException} if originator permanently doesn't have the given * permission. - * Soft (temporary) denials are considered OK for preflight purposes. * - * @param context A {@link Context}, used for permission checks. - * @param identity The identity to check. - * @param permission The identifier of the permission we want to check. + * @param context A {@link Context}, used for permission checks. + * @param identity The identity to check. + * @param permission The identifier of the permission we want to check. + * @param allowSoftDenial If true, the operation succeeds even for soft (temporary) denials. */ + // TODO: Consider splitting up this method instead of using `allowSoftDenial`, to make it + // clearer when soft denials are not allowed. private static void enforcePermissionForPreflight(@NonNull Context context, - @NonNull Identity identity, @NonNull String permission) { + @NonNull Identity identity, @NonNull String permission, boolean allowSoftDenial) { final int status = PermissionUtil.checkPermissionForPreflight(context, identity, permission); switch (status) { case PermissionChecker.PERMISSION_GRANTED: - case PermissionChecker.PERMISSION_SOFT_DENIED: return; + case PermissionChecker.PERMISSION_SOFT_DENIED: + if (allowSoftDenial) { + return; + } // else fall through case PermissionChecker.PERMISSION_HARD_DENIED: throw new SecurityException( String.format("Failed to obtain permission %s for identity %s", permission, diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index f6748de660e2..47aa58751900 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -737,7 +737,7 @@ class ActivityClientController extends IActivityClientController.Stub { synchronized (mGlobalLock) { final ActivityRecord r = ensureValidPictureInPictureActivityParams( "enterPictureInPictureMode", token, params); - return mService.enterPictureInPictureMode(r, params); + return mService.enterPictureInPictureMode(r, params, true /* fromClient */); } } finally { Binder.restoreCallingIdentity(origId); @@ -853,22 +853,23 @@ class ActivityClientController extends IActivityClientController.Stub { /** * Requests that an activity should enter picture-in-picture mode if possible. This method may * be used by the implementation of non-phone form factors. + * + * @return false if the activity cannot enter PIP mode. */ - void requestPictureInPictureMode(@NonNull ActivityRecord r) { + boolean requestPictureInPictureMode(@NonNull ActivityRecord r) { if (r.inPinnedWindowingMode()) { - throw new IllegalStateException("Activity is already in PIP mode"); + return false; } final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState( "requestPictureInPictureMode", /* beforeStopping */ false); if (!canEnterPictureInPicture) { - throw new IllegalStateException( - "Requested PIP on an activity that doesn't support it"); + return false; } if (r.pictureInPictureArgs.isAutoEnterEnabled()) { - mService.enterPictureInPictureMode(r, r.pictureInPictureArgs); - return; + return mService.enterPictureInPictureMode(r, r.pictureInPictureArgs, + false /* fromClient */); } try { @@ -876,9 +877,11 @@ class ActivityClientController extends IActivityClientController.Stub { r.app.getThread(), r.token); transaction.addCallback(EnterPipRequestedItem.obtain()); mService.getLifecycleManager().scheduleTransaction(transaction); + return true; } catch (Exception e) { Slog.w(TAG, "Failed to send enter pip requested item: " + r.intent.getComponent(), e); + return false; } } diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index d6c0ab6b124b..622de57a1078 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -68,6 +68,7 @@ import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_METRICS; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT; import static com.android.server.wm.EventLogTags.WM_ACTIVITY_LAUNCH_TIME; @@ -101,6 +102,7 @@ import android.util.TimeUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.util.FrameworkStatsLog; +import com.android.internal.util.LatencyTracker; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; @@ -780,6 +782,12 @@ class ActivityMetricsLogger { info.mReason = activityToReason.valueAt(index); info.mLoggedTransitionStarting = true; if (info.mIsDrawn) { + if (info.mReason == APP_TRANSITION_RECENTS_ANIM) { + final LatencyTracker latencyTracker = r.mWmService.mLatencyTracker; + final int duration = info.mSourceEventDelayMs + info.mCurrentTransitionDelayMs; + mLoggerHandler.post(() -> latencyTracker.logAction( + LatencyTracker.ACTION_START_RECENTS_ANIMATION, duration)); + } done(false /* abort */, info, "notifyTransitionStarting drawn", timestampNs); } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 59c6cd782d3b..398926f0ca85 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -432,6 +432,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private static final int DESTROY_TIMEOUT = 10 * 1000; final ActivityTaskManagerService mAtmService; + @NonNull final ActivityInfo info; // activity info provided by developer in AndroidManifest // Which user is this running for? final int mUserId; @@ -2786,7 +2787,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @VisibleForTesting boolean canLaunchHomeActivity(int uid, ActivityRecord sourceRecord) { - if (uid == Process.myUid() || uid == 0) { + if (uid == SYSTEM_UID || uid == 0) { // System process can launch home activity. return true; } @@ -3005,25 +3006,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A && info.supportsPictureInPicture(); } - /** - * @return whether this activity supports split-screen multi-window and can be put in - * split-screen. - */ - @Override - public boolean supportsSplitScreenWindowingMode() { - return supportsSplitScreenWindowingModeInDisplayArea(getDisplayArea()); - } - - /** - * @return whether this activity supports split-screen multi-window and can be put in - * split-screen if it is in the given {@link TaskDisplayArea}. - */ - boolean supportsSplitScreenWindowingModeInDisplayArea(@Nullable TaskDisplayArea tda) { - return super.supportsSplitScreenWindowingMode() - && mAtmService.mSupportsSplitScreenMultiWindow - && supportsMultiWindowInDisplayArea(tda); - } - boolean supportsFreeform() { return supportsFreeformInDisplayArea(getDisplayArea()); } @@ -4474,12 +4456,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * @return Whether we are allowed to show non-starting windows at the moment. We disallow - * showing windows during transitions in case we have windows that have wide-color-gamut - * color mode set to avoid jank in the middle of the transition. + * showing windows while the transition animation is playing in case we have windows + * that have wide-color-gamut color mode set to avoid jank in the middle of the + * animation. */ boolean canShowWindows() { - return allDrawn && !(isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION) - && hasNonDefaultColorWindow()); + final boolean drawn = mTransitionController.isShellTransitionsEnabled() + ? mSyncState != SYNC_STATE_WAITING_FOR_DRAW : allDrawn; + final boolean animating = mTransitionController.isShellTransitionsEnabled() + ? mTransitionController.inPlayingTransition(this) + : isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION); + return drawn && !(animating && hasNonDefaultColorWindow()); } /** @@ -5535,7 +5522,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A wasStopped, this); mAppStopped = false; // Allow the window to turn the screen on once the app is resumed again. - setCurrentLaunchCanTurnScreenOn(true); + if (mAtmService.getActivityStartController().isInExecution()) { + setCurrentLaunchCanTurnScreenOn(true); + } + if (!wasStopped) { destroySurfaces(true /*cleanupOnResume*/); } @@ -7739,7 +7729,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (isFixedOrientationLetterboxAllowed || mCompatDisplayInsets != null // In fullscreen, can be letterboxed for aspect ratio. || !inMultiWindowMode()) { - updateResolvedBoundsHorizontalPosition(newParentConfiguration); + updateResolvedBoundsPosition(newParentConfiguration); } if (mVisibleRequested) { @@ -7842,39 +7832,60 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** - * Adjusts horizontal position of resolved bounds if they doesn't fill the parent using gravity + * Adjusts position of resolved bounds if they doesn't fill the parent using gravity * requested in the config or via an ADB command. For more context see {@link - * LetterboxUiController#getHorizontalPositionMultiplier(Configuration)}. + * LetterboxUiController#getHorizontalPositionMultiplier(Configuration)} and + * {@link LetterboxUiController#getVerticalPositionMultiplier(Configuration)} */ - private void updateResolvedBoundsHorizontalPosition(Configuration newParentConfiguration) { + private void updateResolvedBoundsPosition(Configuration newParentConfiguration) { final Configuration resolvedConfig = getResolvedOverrideConfiguration(); final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds(); final Rect screenResolvedBounds = mSizeCompatBounds != null ? mSizeCompatBounds : resolvedBounds; final Rect parentAppBounds = newParentConfiguration.windowConfiguration.getAppBounds(); final Rect parentBounds = newParentConfiguration.windowConfiguration.getBounds(); - if (resolvedBounds.isEmpty() || parentBounds.width() == screenResolvedBounds.width()) { + if (resolvedBounds.isEmpty()) { return; } - + // Horizontal position int offsetX = 0; - if (screenResolvedBounds.width() >= parentAppBounds.width()) { - // If resolved bounds overlap with insets, center within app bounds. - offsetX = getHorizontalCenterOffset( - parentAppBounds.width(), screenResolvedBounds.width()); - } else { - float positionMultiplier = - mLetterboxUiController.getHorizontalPositionMultiplier(newParentConfiguration); - offsetX = (int) Math.ceil((parentAppBounds.width() - screenResolvedBounds.width()) - * positionMultiplier); + if (parentBounds.width() != screenResolvedBounds.width()) { + if (screenResolvedBounds.width() >= parentAppBounds.width()) { + // If resolved bounds overlap with insets, center within app bounds. + offsetX = getCenterOffset( + parentAppBounds.width(), screenResolvedBounds.width()); + } else { + float positionMultiplier = + mLetterboxUiController.getHorizontalPositionMultiplier( + newParentConfiguration); + offsetX = (int) Math.ceil((parentAppBounds.width() - screenResolvedBounds.width()) + * positionMultiplier); + } + } + + // Vertical position + int offsetY = 0; + if (parentBounds.height() != screenResolvedBounds.height()) { + if (screenResolvedBounds.height() >= parentAppBounds.height()) { + // If resolved bounds overlap with insets, center within app bounds. + offsetY = getCenterOffset( + parentAppBounds.height(), screenResolvedBounds.height()); + } else { + float positionMultiplier = + mLetterboxUiController.getVerticalPositionMultiplier( + newParentConfiguration); + offsetY = (int) Math.ceil((parentAppBounds.height() - screenResolvedBounds.height()) + * positionMultiplier); + } } if (mSizeCompatBounds != null) { - mSizeCompatBounds.offset(offsetX, 0 /* offsetY */); + mSizeCompatBounds.offset(offsetX , offsetY); + final int dy = mSizeCompatBounds.top - resolvedBounds.top; final int dx = mSizeCompatBounds.left - resolvedBounds.left; - offsetBounds(resolvedConfig, dx, 0 /* offsetY */); + offsetBounds(resolvedConfig, dx, dy); } else { - offsetBounds(resolvedConfig, offsetX, 0 /* offsetY */); + offsetBounds(resolvedConfig, offsetX, offsetY); } // Since bounds has changed, the configuration needs to be computed accordingly. @@ -7902,6 +7913,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return mLetterboxBoundsForFixedOrientationAndAspectRatio != null; } + boolean isAspectRatioApplied() { + return mIsAspectRatioApplied; + } + /** * Whether this activity is eligible for letterbox eduction. * @@ -8073,21 +8088,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A resolvedBounds.set(containingBounds); final float letterboxAspectRatioOverride = - mLetterboxUiController.getFixedOrientationLetterboxAspectRatio(newParentConfig); + mWmService.mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); final float desiredAspectRatio = letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO ? letterboxAspectRatioOverride : computeAspectRatio(parentBounds); // Apply aspect ratio to resolved bounds mIsAspectRatioApplied = applyAspectRatio(resolvedBounds, containingBoundsWithInsets, - containingBounds, desiredAspectRatio, true); - - // Vertically center if orientation is landscape. Center within parent bounds with insets - // to ensure that insets do not trim height. Bounds will later be horizontally centered in - // {@link updateResolvedBoundsHorizontalPosition()} regardless of orientation. - if (forcedOrientation == ORIENTATION_LANDSCAPE) { - final int offsetY = parentBoundsWithInsets.centerY() - resolvedBounds.centerY(); - resolvedBounds.offset(0, offsetY); - } + containingBounds, desiredAspectRatio); if (mCompatDisplayInsets != null) { mCompatDisplayInsets.getBoundsByRotation( @@ -8111,10 +8118,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A /** * Resolves aspect ratio restrictions for an activity. If the bounds are restricted by - * aspect ratio, the position will be adjusted later in {@link - * updateResolvedBoundsHorizontalPosition} within parent's app bounds to balance the visual - * appearance. The policy of aspect ratio has higher priority than the requested override - * bounds. + * aspect ratio, the position will be adjusted later in {@link #updateResolvedBoundsPosition + * within parent's app bounds to balance the visual appearance. The policy of aspect ratio has + * higher priority than the requested override bounds. */ private void resolveAspectRatioRestriction(Configuration newParentConfiguration) { final Configuration resolvedConfig = getResolvedOverrideConfiguration(); @@ -8126,7 +8132,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mTmpBounds.setEmpty(); mIsAspectRatioApplied = applyAspectRatio(mTmpBounds, parentAppBounds, parentBounds); // If the out bounds is not empty, it means the activity cannot fill parent's app bounds, - // then they should be aligned later in #updateResolvedBoundsHorizontalPosition(). + // then they should be aligned later in #updateResolvedBoundsPosition() if (!mTmpBounds.isEmpty()) { resolvedBounds.set(mTmpBounds); } @@ -8260,22 +8266,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A forAllWindows(WindowState::updateGlobalScale, false /* traverseTopToBottom */); } - // Vertically center within parent (bounds) - this is a UX choice and exclude the horizontal - // decor if needed. Horizontal position is adjusted in - // updateResolvedBoundsHorizontalPosition. + // The position will be later adjusted in updateResolvedBoundsPosition. // Above coordinates are in "@" space, now place "*" and "#" to screen space. final boolean fillContainer = resolvedBounds.equals(containingBounds); final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left; - // If the activity is not in size compat mode, calculate vertical centering - // from the container and resolved bounds. - // If the activity is in size compat mode, calculate vertical centering - // from the container and size compat bounds. - // The container bounds contain the parent bounds offset in the display, for - // example when an activity is in the lower split of split screen. - final int screenPosY = (mSizeCompatBounds == null - ? (containerBounds.height() - resolvedBounds.height()) / 2 - : (containerBounds.height() - mSizeCompatBounds.height()) / 2) - + containerBounds.top; + final int screenPosY = fillContainer ? containerBounds.top : containerAppBounds.top; if (screenPosX != 0 || screenPosY != 0) { if (mSizeCompatBounds != null) { @@ -8335,9 +8330,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; } - /** @return The horizontal offset of putting the content in the center of viewport. */ - private static int getHorizontalCenterOffset(int viewportW, int contentW) { - return (int) ((viewportW - contentW + 1) * 0.5f); + /** @return The horizontal / vertical offset of putting the content in the center of viewport.*/ + private static int getCenterOffset(int viewportDim, int contentDim) { + return (int) ((viewportDim - contentDim + 1) * 0.5f); } private static void offsetBounds(Configuration inOutConfig, int offsetX, int offsetY) { @@ -8525,7 +8520,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, Rect containingBounds) { return applyAspectRatio(outBounds, containingAppBounds, containingBounds, - 0 /* desiredAspectRatio */, false /* fixedOrientationLetterboxed */); + 0 /* desiredAspectRatio */); } /** @@ -8536,23 +8531,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A */ // TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer. private boolean applyAspectRatio(Rect outBounds, Rect containingAppBounds, - Rect containingBounds, float desiredAspectRatio, boolean fixedOrientationLetterboxed) { + Rect containingBounds, float desiredAspectRatio) { final float maxAspectRatio = info.getMaxAspectRatio(); final Task rootTask = getRootTask(); final float minAspectRatio = getMinAspectRatio(); - // Not using ActivityRecord#isResizeable() directly because app compatibility testing - // showed that android:supportsPictureInPicture="true" alone is not sufficient signal for - // not letterboxing an app. - // TODO(214602463): Remove multi-window check since orientation and aspect ratio - // restrictions should always be applied in multi-window. + final TaskFragment organizedTf = getOrganizedTaskFragment(); if (task == null || rootTask == null - || (inMultiWindowMode() && isResizeable(/* checkPictureInPictureSupport */ false) - && !fixedOrientationLetterboxed) || (maxAspectRatio < 1 && minAspectRatio < 1 && desiredAspectRatio < 1) - || isInVrUiMode(getConfiguration())) { - // We don't enforce aspect ratio if the activity task is in multiwindow unless it is in - // size-compat mode or is letterboxed from fixed orientation. We also don't set it if we - // are in VR mode. + // Don't set aspect ratio if we are in VR mode. + || isInVrUiMode(getConfiguration()) + // TODO(b/232898850): Always respect aspect ratio requests. + // Don't set aspect ratio for activity in ActivityEmbedding split. + || (organizedTf != null && !organizedTf.fillsParent())) { return false; } @@ -8651,7 +8641,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * Returns the min aspect ratio of this activity. */ private float getMinAspectRatio() { - return info.getMinAspectRatio(getRequestedOrientation()); + float infoAspectRatio = info.getMinAspectRatio(getRequestedOrientation()); + // Complying with the CDD 7.1.1.2 requirement for unresizble apps: + // https://source.android.com/compatibility/12/android-12-cdd#7112_screen_aspect_ratio + return infoAspectRatio < 1f && info.resizeMode == RESIZE_MODE_UNRESIZEABLE + // TODO(233582832): Consider removing fixed-orientation condition. + // Some apps switching from tablet to phone layout at the certain size + // threshold. This may lead to flickering on tablets in landscape orientation + // if an app sets orientation to portrait dynamically because of aspect ratio + // restriction applied here. + && getRequestedConfigurationOrientation() != ORIENTATION_UNDEFINED + ? mLetterboxUiController.getDefaultMinAspectRatioForUnresizableApps() + : infoAspectRatio; } /** @@ -9600,7 +9601,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A outBounds.bottom = dH; outBounds.right = (int) ((float) dH * dH / dW); } - outBounds.offset(getHorizontalCenterOffset(mWidth, outBounds.width()), 0 /* dy */); + outBounds.offset(getCenterOffset(mWidth, outBounds.width()), 0 /* dy */); } outAppBounds.set(outBounds); @@ -9680,6 +9681,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A @Override boolean isSyncFinished() { if (!super.isSyncFinished()) return false; + if (mDisplayContent != null && mDisplayContent.mUnknownAppVisibilityController + .isVisibilityUnknown(this)) { + return false; + } if (!isVisibleRequested()) return true; // Wait for attach. That is the earliest time where we know if there will be an associated // display rotation. If we don't wait, the starting-window can finishDrawing first and @@ -9707,6 +9712,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return true; } + @Nullable + Point getMinDimensions() { + final ActivityInfo.WindowLayout windowLayout = info.windowLayout; + if (windowLayout == null) { + return null; + } + return new Point(windowLayout.minWidth, windowLayout.minHeight); + } + static class Builder { private final ActivityTaskManagerService mAtmService; private WindowProcessController mCallerApp; diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java index fc22e2decbef..d59f69622f15 100644 --- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java +++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java @@ -19,7 +19,6 @@ package com.android.server.wm; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.os.InputConfig; -import android.os.Process; import android.view.InputWindowHandle; import android.view.SurfaceControl; import android.view.WindowManager; @@ -102,8 +101,8 @@ class ActivityRecordInputSink { inputWindowHandle.replaceTouchableRegionWithCrop = true; inputWindowHandle.name = mName; inputWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; - inputWindowHandle.ownerUid = Process.myUid(); - inputWindowHandle.ownerPid = Process.myPid(); + inputWindowHandle.ownerPid = WindowManagerService.MY_PID; + inputWindowHandle.ownerUid = WindowManagerService.MY_UID; inputWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.NO_INPUT_CHANNEL; return inputWindowHandle; } diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index d60981fcf504..a78dbd6bbe60 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -90,6 +90,8 @@ public class ActivityStartController { boolean mCheckedForSetup = false; + private boolean mInExecution = false; + /** * TODO(b/64750076): Capture information necessary for dump and * {@link #postStartActivityProcessingForLastStarter} rather than keeping the entire object @@ -123,7 +125,15 @@ public class ActivityStartController { return mFactory.obtain().setIntent(intent).setReason(reason); } + void onExecutionStarted(ActivityStarter starter) { + mInExecution = true; + } + + boolean isInExecution() { + return mInExecution; + } void onExecutionComplete(ActivityStarter starter) { + mInExecution = false; if (mLastStarter == null) { mLastStarter = mFactory.obtain(); } @@ -508,7 +518,8 @@ public class ActivityStartController { */ int startActivityInTaskFragment(@NonNull TaskFragment taskFragment, @NonNull Intent activityIntent, @Nullable Bundle activityOptions, - @Nullable IBinder resultTo, int callingUid, int callingPid) { + @Nullable IBinder resultTo, int callingUid, int callingPid, + @Nullable IBinder errorCallbackToken) { final ActivityRecord caller = resultTo != null ? ActivityRecord.forTokenLocked(resultTo) : null; return obtainStarter(activityIntent, "startActivityInTaskFragment") @@ -521,6 +532,7 @@ public class ActivityStartController { .setRealCallingUid(callingUid) .setRealCallingPid(callingPid) .setUserId(caller != null ? caller.mUserId : mService.getCurrentUserId()) + .setErrorCallbackToken(errorCallbackToken) .execute(); } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index fb87576ba809..a30c7445225b 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -51,6 +51,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE_PER_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP; +import static android.content.pm.ActivityInfo.launchModeToString; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.INVALID_UID; import static android.view.Display.DEFAULT_DISPLAY; @@ -89,7 +90,6 @@ import android.app.PendingIntent; import android.app.ProfilerInfo; import android.app.WaitResult; import android.app.WindowConfiguration; -import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.content.ComponentName; @@ -212,6 +212,7 @@ class ActivityStarter { // The task which was above the targetTask before starting this activity. null if the targetTask // was already on top or if the activity is in a new task. private Task mPriorAboveTask; + private boolean mDisplayLockAndOccluded; // We must track when we deliver the new intent since multiple code paths invoke // {@link #deliverNewIntent}. This is due to early returns in the code path. This flag is used @@ -369,6 +370,13 @@ class ActivityStarter { int filterCallingUid; PendingIntentRecord originatingPendingIntent; boolean allowBackgroundActivityStart; + /** + * The error callback token passed in {@link android.window.WindowContainerTransaction} + * for TaskFragment operation error handling via + * {@link android.window.TaskFragmentOrganizer#onTaskFragmentError(IBinder, Throwable)}. + */ + @Nullable + IBinder errorCallbackToken; /** * If set to {@code true}, allows this activity start to look into @@ -422,6 +430,7 @@ class ActivityStarter { filterCallingUid = UserHandle.USER_NULL; originatingPendingIntent = null; allowBackgroundActivityStart = false; + errorCallbackToken = null; } /** @@ -464,6 +473,7 @@ class ActivityStarter { filterCallingUid = request.filterCallingUid; originatingPendingIntent = request.originatingPendingIntent; allowBackgroundActivityStart = request.allowBackgroundActivityStart; + errorCallbackToken = request.errorCallbackToken; } /** @@ -634,6 +644,8 @@ class ActivityStarter { */ int execute() { try { + onExecutionStarted(); + // Refuse possible leaked file descriptors if (mRequest.intent != null && mRequest.intent.hasFileDescriptors()) { throw new IllegalArgumentException("File descriptors passed in Intent"); @@ -1248,6 +1260,10 @@ class ActivityStarter { mController.onExecutionComplete(this); } + private void onExecutionStarted() { + mController.onExecutionStarted(this); + } + private boolean isHomeApp(int uid, @Nullable String packageName) { if (mService.mHomeProcess != null) { // Fast check @@ -1668,6 +1684,7 @@ class ActivityStarter { private @Nullable Task handleStartResult(@NonNull ActivityRecord started, ActivityOptions options, int result, Transition newTransition, RemoteTransition remoteTransition) { + final boolean userLeaving = mSupervisor.mUserLeaving; mSupervisor.mUserLeaving = false; final Task currentRootTask = started.getRootTask(); final Task startedActivityRootTask = @@ -1686,7 +1703,8 @@ class ActivityStarter { // Root task should also be detached from display and be removed if it's empty. if (startedActivityRootTask != null && startedActivityRootTask.isAttached() && !startedActivityRootTask.hasActivity() - && !startedActivityRootTask.isActivityTypeHome()) { + && !startedActivityRootTask.isActivityTypeHome() + && !startedActivityRootTask.mCreatedByOrganizer) { startedActivityRootTask.removeIfPossible("handleStartResult"); } if (newTransition != null) { @@ -1727,19 +1745,42 @@ class ActivityStarter { // Transition housekeeping. final TransitionController transitionController = started.mTransitionController; final boolean isStarted = result == START_SUCCESS || result == START_TASK_TO_FRONT; + final boolean isTransientLaunch = options != null && options.getTransientLaunch(); + // Start transient launch while keyguard locked and occluded by other app, for this + // condition we would like to play the remote transition without modify any visible state + // for the hierarchy in core, so here will force execute this transition. + final boolean forceTransientTransition = isTransientLaunch && mPriorAboveTask != null + && mDisplayLockAndOccluded; if (isStarted) { // The activity is started new rather than just brought forward, so record it as an // existence change. transitionController.collectExistenceChange(started); } else if (result == START_DELIVERED_TO_TOP && newTransition != null) { // We just delivered to top, so there isn't an actual transition here. - newTransition.abort(); - newTransition = null; + if (!forceTransientTransition) { + newTransition.abort(); + newTransition = null; + } } - if (options != null && options.getTransientLaunch()) { + if (isTransientLaunch) { + if (forceTransientTransition && newTransition != null) { + newTransition.collect(mLastStartActivityRecord); + newTransition.collect(mPriorAboveTask); + } // `started` isn't guaranteed to be the actual relevant activity, so we must wait // until after we launched to identify the relevant activity. transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask); + if (forceTransientTransition && newTransition != null) { + final DisplayContent dc = mLastStartActivityRecord.getDisplayContent(); + // update wallpaper target to TransientHide + dc.mWallpaperController.adjustWallpaperWindows(); + // execute transition because there is no change + newTransition.setReady(dc, true /* ready */); + } + } + if (!userLeaving) { + // no-user-leaving implies not entering PiP. + transitionController.setCanPipOnFinish(false /* canPipOnFinish */); } if (newTransition != null) { transitionController.requestStartTransition(newTransition, @@ -2031,8 +2072,20 @@ class ActivityStarter { } if (mInTaskFragment != null && !canEmbedActivity(mInTaskFragment, r, newTask, targetTask)) { - Slog.e(TAG, "Permission denied: Cannot embed " + r + " to " + mInTaskFragment.getTask() - + " targetTask= " + targetTask); + final StringBuilder errorMsg = new StringBuilder("Permission denied: Cannot embed " + r + + " to " + mInTaskFragment.getTask() + ". newTask=" + newTask + ", targetTask= " + + targetTask); + if (newTask && isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, + LAUNCH_SINGLE_INSTANCE_PER_TASK, LAUNCH_SINGLE_TASK)) { + errorMsg.append("\nActivity tries to launch on a new task because the launch mode" + + " is " + launchModeToString(mLaunchMode)); + } else if (newTask && (mLaunchFlags & (FLAG_ACTIVITY_NEW_DOCUMENT + | FLAG_ACTIVITY_NEW_TASK)) != 0) { + errorMsg.append("\nActivity tries to launch on a new task because the launch flags" + + " contains FLAG_ACTIVITY_NEW_DOCUMENT or FLAG_ACTIVITY_NEW_TASK. " + + "mLaunchFlag=" + mLaunchFlags); + } + Slog.e(TAG, errorMsg.toString()); return START_PERMISSION_DENIED; } @@ -2380,6 +2433,7 @@ class ActivityStarter { mAvoidMoveToFront = false; mFrozeTaskList = false; mTransientLaunch = false; + mDisplayLockAndOccluded = false; mVoiceSession = null; mVoiceInteractor = null; @@ -2447,6 +2501,11 @@ class ActivityStarter { } } + if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0 && mSourceRecord == null) { + // ignore the flag if there is no the sourceRecord + mLaunchFlags &= ~FLAG_ACTIVITY_LAUNCH_ADJACENT; + } + // We'll invoke onUserLeaving before onPause only if the launching // activity did not explicitly state that this is an automated launch. mSupervisor.mUserLeaving = (mLaunchFlags & FLAG_ACTIVITY_NO_USER_ACTION) == 0; @@ -2483,6 +2542,16 @@ class ActivityStarter { mAvoidMoveToFront = true; } mTransientLaunch = mOptions.getTransientLaunch(); + final KeyguardController kc = mSupervisor.getKeyguardController(); + final int displayId = mPreferredTaskDisplayArea.getDisplayId(); + mDisplayLockAndOccluded = kc.isKeyguardLocked(displayId) + && kc.isDisplayOccluded(displayId); + // Recents animation on lock screen, do not resume & move launcher to top. + if (mTransientLaunch && mDisplayLockAndOccluded + && mService.getTransitionController().isShellTransitionsEnabled()) { + mDoResume = false; + mAvoidMoveToFront = true; + } mTargetRootTask = Task.fromWindowContainerToken(mOptions.getLaunchRootTask()); if (inTaskFragment == null) { @@ -2750,17 +2819,15 @@ class ActivityStarter { mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, intentTask, mOptions); } - } else { - // If a launch target indicated, and the matching task is already in the adjacent task - // of the launch target. Adjust to use the adjacent task as its launch target. So the - // existing task will be launched into the closer one and won't be reparent redundantly. - // TODO(b/231541706): Migrate the logic to wm-shell after having proper APIs to help - // resolve target task without actually starting the activity. - final Task adjacentTargetTask = mTargetRootTask.getAdjacentTaskFragment() != null - ? mTargetRootTask.getAdjacentTaskFragment().asTask() : null; - if (adjacentTargetTask != null && intentActivity.isDescendantOf(adjacentTargetTask)) { - mTargetRootTask = adjacentTargetTask; - } + } + + // If the matching task is already in the adjacent task of the launch target. Adjust to use + // the adjacent task as its launch target. So the existing task will be launched into the + // closer one and won't be reparent redundantly. + final Task adjacentTargetTask = mTargetRootTask.getAdjacentTaskFragment() != null + ? mTargetRootTask.getAdjacentTaskFragment().asTask() : null; + if (adjacentTargetTask != null && intentActivity.isDescendantOf(adjacentTargetTask)) { + mTargetRootTask = adjacentTargetTask; } // If the target task is not in the front, then we need to bring it to the front... @@ -2889,6 +2956,7 @@ class ActivityStarter { private void addOrReparentStartingActivity(@NonNull Task task, String reason) { TaskFragment newParent = task; if (mInTaskFragment != null) { + // TODO(b/234351413): remove remaining embedded Task logic. // mInTaskFragment is created and added to the leaf task by task fragment organizer's // request. If the task was resolved and different than mInTaskFragment, reparent the // task to mInTaskFragment for embedding. @@ -2915,7 +2983,14 @@ class ActivityStarter { newParent = candidateTf; } } - + // Start Activity to the Task if mStartActivity's min dimensions are not satisfied. + if (newParent.isEmbedded() && newParent.smallerThanMinDimension(mStartActivity)) { + reason += " - MinimumDimensionViolation"; + mService.mWindowOrganizerController.sendMinimumDimensionViolation( + newParent, mStartActivity.getMinDimensions(), mRequest.errorCallbackToken, + reason); + newParent = task; + } if (mStartActivity.getTaskFragment() == null || mStartActivity.getTaskFragment() == newParent) { newParent.addChild(mStartActivity, POSITION_TOP); @@ -3200,6 +3275,11 @@ class ActivityStarter { return this; } + ActivityStarter setErrorCallbackToken(@Nullable IBinder errorCallbackToken) { + mRequest.errorCallbackToken = errorCallbackToken; + return this; + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("mCurrentUser="); @@ -3234,12 +3314,8 @@ class ActivityStarter { pw.println(mOptions); } pw.print(prefix); - pw.print("mLaunchSingleTop="); - pw.print(LAUNCH_SINGLE_TOP == mLaunchMode); - pw.print(" mLaunchSingleInstance="); - pw.print(LAUNCH_SINGLE_INSTANCE == mLaunchMode); - pw.print(" mLaunchSingleTask="); - pw.println(LAUNCH_SINGLE_TASK == mLaunchMode); + pw.print("mLaunchMode="); + pw.print(launchModeToString(mLaunchMode)); pw.print(prefix); pw.print("mLaunchFlags=0x"); pw.print(Integer.toHexString(mLaunchFlags)); diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index f75d73b04476..6152676b9be7 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -666,9 +666,6 @@ public abstract class ActivityTaskManagerInternal { public abstract boolean hasSystemAlertWindowPermission(int callingUid, int callingPid, String callingPackage); - /** Called when the device is waking up */ - public abstract void notifyWakingUp(); - /** * Registers a callback which can intercept activity starts. * @throws IllegalArgumentException if duplicate ids are provided diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index b8162cd3d008..6ae8887a7912 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -64,8 +64,8 @@ import static android.provider.Settings.Global.HIDE_ERROR_DIALOGS; import static android.provider.Settings.System.FONT_SCALE; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT; -import static android.view.WindowManager.TRANSIT_WAKE; +import static android.view.WindowManager.TRANSIT_PIP; +import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS; @@ -73,7 +73,6 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; import static com.android.server.am.ActivityManagerService.ANR_TRACE_DIR; -import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS; import static com.android.server.am.ActivityManagerService.dumpStackTraces; import static com.android.server.am.ActivityManagerServiceDumpActivitiesProto.ROOT_WINDOW_CONTAINER; @@ -95,6 +94,7 @@ import static com.android.server.am.EventLogTags.writeBootProgressEnableScreen; import static com.android.server.am.EventLogTags.writeConfigurationChanged; import static com.android.server.wm.ActivityInterceptorCallback.FIRST_ORDERED_ID; import static com.android.server.wm.ActivityInterceptorCallback.LAST_ORDERED_ID; +import static com.android.server.wm.ActivityRecord.State.PAUSING; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_ROOT_TASK; import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_SWITCH; @@ -116,6 +116,7 @@ import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_O import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_ONLY; import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS; import static com.android.server.wm.Task.REPARENT_KEEP_ROOT_TASK_AT_FRONT; +import static com.android.server.wm.WindowManagerService.MY_PID; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import android.Manifest; @@ -238,6 +239,7 @@ import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.TransferPipe; import com.android.internal.policy.AttributeCache; import com.android.internal.policy.KeyguardDismissCallback; +import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; @@ -400,6 +402,13 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** The time at which the previous process was last visible. */ private long mPreviousProcessVisibleTime; + /** + * It can be true from keyguard-going-away to set-keyguard-shown. And getTopProcessState() will + * return {@link ActivityManager#PROCESS_STATE_IMPORTANT_FOREGROUND} to avoid top app from + * preempting CPU while keyguard is animating. + */ + private volatile boolean mDemoteTopAppDuringUnlocking; + /** List of intents that were used to start the most recent tasks. */ private RecentTasks mRecentTasks; /** State of external calls telling us if the device is awake or asleep. */ @@ -2830,12 +2839,24 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { keyguardShowing); mH.sendMessage(msg); } + // Always reset the state regardless of keyguard-showing change, because that means the + // unlock is either completed or canceled. + if (mDemoteTopAppDuringUnlocking) { + mDemoteTopAppDuringUnlocking = false; + // The scheduling group of top process was demoted by unlocking, so recompute + // to restore its real top priority if possible. + if (mTopApp != null) { + mTopApp.scheduleUpdateOomAdj(); + } + } try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "setLockScreenShown"); mRootWindowContainer.forAllDisplays(displayContent -> { mKeyguardController.setKeyguardShown(displayContent.getDisplayId(), keyguardShowing, aodShowing); }); } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); Binder.restoreCallingIdentity(ident); } } @@ -2862,6 +2883,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { // animation of system UI. Even if AOD is not enabled, it should be no harm. final WindowProcessController proc; synchronized (mGlobalLockWithoutBoost) { + mDemoteTopAppDuringUnlocking = false; final WindowState notificationShade = mRootWindowContainer.getDefaultDisplay() .getDisplayPolicy().getNotificationShade(); proc = notificationShade != null @@ -3399,8 +3421,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { synchronized (mGlobalLock) { // Keyguard asked us to clear the home task snapshot before going away, so do that. - if ((flags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) { + if ((flags & KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT) != 0) { mActivityClientController.invalidateHomeTaskSnapshot(null /* token */); + } else if (mKeyguardShown) { + // Only set if it is not unlocking to launcher which may also animate. + mDemoteTopAppDuringUnlocking = true; } mRootWindowContainer.forAllDisplays(displayContent -> { @@ -3448,10 +3473,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { /** * Puts the given activity in picture in picture mode if possible. * + * @param fromClient true if this comes from a client call (eg. Activity.enterPip). * @return true if the activity is now in picture-in-picture mode, or false if it could not * enter picture-in-picture mode. */ - boolean enterPictureInPictureMode(@NonNull ActivityRecord r, PictureInPictureParams params) { + boolean enterPictureInPictureMode(@NonNull ActivityRecord r, + @NonNull PictureInPictureParams params, boolean fromClient) { // If the activity is already in picture in picture mode, then just return early if (r.inPinnedWindowingMode()) { return true; @@ -3464,6 +3491,21 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return false; } + // If the app is using legacy-entry (not auto-enter), then we will get a client-request + // that was actually a server-request (via pause(userLeaving=true)). This happens when + // the app is PAUSING, so detect that case here. + boolean originallyFromClient = fromClient + && (!r.isState(PAUSING) || params.isAutoEnterEnabled()); + + // Create a transition only for this pip entry if it is coming from the app without the + // system requesting that the app enter-pip. If the system requested it, that means it + // should be part of that transition if possible. + final Transition transition = + (getTransitionController().isShellTransitionsEnabled() && originallyFromClient) + ? new Transition(TRANSIT_PIP, 0 /* flags */, + getTransitionController(), mWindowManager.mSyncEngine) + : null; + final Runnable enterPipRunnable = () -> { synchronized (mGlobalLock) { if (r.getParent() == null) { @@ -3472,7 +3514,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } r.setPictureInPictureParams(params); mRootWindowContainer.moveActivityToPinnedRootTask(r, - null /* launchIntoPipHostActivity */, "enterPictureInPictureMode"); + null /* launchIntoPipHostActivity */, "enterPictureInPictureMode", + transition); final Task task = r.getTask(); // Continue the pausing process after entering pip. if (task.getPausingActivity() == r) { @@ -3489,12 +3532,36 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mActivityClientController.dismissKeyguard(r.token, new KeyguardDismissCallback() { @Override public void onDismissSucceeded() { - mH.post(enterPipRunnable); + if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) { + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, + "Creating Pending Pip-Enter: %s", transition); + mWindowManager.mSyncEngine.queueSyncSet( + () -> getTransitionController().moveToCollecting(transition), + enterPipRunnable); + } else { + // Move to collecting immediately to "claim" the sync-engine for this + // transition. + if (transition != null) { + getTransitionController().moveToCollecting(transition); + } + mH.post(enterPipRunnable); + } } }, null /* message */); } else { // Enter picture in picture immediately otherwise - enterPipRunnable.run(); + if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) { + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, + "Creating Pending Pip-Enter: %s", transition); + mWindowManager.mSyncEngine.queueSyncSet( + () -> getTransitionController().moveToCollecting(transition), + enterPipRunnable); + } else { + if (transition != null) { + getTransitionController().moveToCollecting(transition); + } + enterPipRunnable.run(); + } } return true; } @@ -5595,12 +5662,17 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @HotPath(caller = HotPath.OOM_ADJUSTMENT) @Override public int getTopProcessState() { + final int topState = mTopProcessState; + if (mDemoteTopAppDuringUnlocking && topState == ActivityManager.PROCESS_STATE_TOP) { + // The unlocking UI is more important, so defer the top state of app. + return ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; + } if (mRetainPowerModeAndTopProcessState) { // There is a launching app while device may be sleeping, force the top state so // the launching process can have top-app scheduling group. return ActivityManager.PROCESS_STATE_TOP; } - return mTopProcessState; + return topState; } @HotPath(caller = HotPath.PROCESS_CHANGE) @@ -6624,15 +6696,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } @Override - public void notifyWakingUp() { - synchronized (mGlobalLock) { - // Start a transition for waking. This is needed for showWhenLocked activities. - getTransitionController().requestTransitionIfNeeded(TRANSIT_WAKE, 0 /* flags */, - null /* trigger */, mRootWindowContainer.getDefaultDisplay()); - } - } - - @Override public void registerActivityStartInterceptor( @ActivityInterceptorCallback.OrderedId int id, ActivityInterceptorCallback callback) { diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 35f977de8290..bd2ce95a814f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -42,6 +42,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static android.os.Process.INVALID_UID; +import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_TO_FRONT; @@ -93,6 +94,7 @@ import android.app.AppOpsManagerInternal; import android.app.IActivityClientController; import android.app.ProfilerInfo; import android.app.ResultInfo; +import android.app.TaskInfo; import android.app.WaitResult; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ClientTransaction; @@ -121,7 +123,6 @@ import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManager; -import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.Trace; @@ -155,6 +156,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; // TODO: This class has become a dumping ground. Let's // - Move things relating to the hierarchy to RootWindowContainer @@ -246,6 +248,9 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { /** Helper class to abstract out logic for fetching the set of currently running tasks */ private RunningTasks mRunningTasks; + /** Helper for {@link Task#fillTaskInfo}. */ + final TaskInfoHelper mTaskInfoHelper = new TaskInfoHelper(); + private final ActivityTaskSupervisorHandler mHandler; final Looper mLooper; @@ -1316,7 +1321,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } void acquireLaunchWakelock() { - if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) { + if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != SYSTEM_UID) { throw new IllegalStateException("Calling must be system uid"); } mLaunchingActivityWakeLock.acquire(); @@ -1389,8 +1394,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { if (mLaunchingActivityWakeLock.isHeld()) { mHandler.removeMessages(LAUNCH_TIMEOUT_MSG); - if (VALIDATE_WAKE_LOCK_CALLER && - Binder.getCallingUid() != Process.myUid()) { + if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != SYSTEM_UID) { throw new IllegalStateException("Calling must be system uid"); } mLaunchingActivityWakeLock.release(); @@ -1800,7 +1804,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { if (!mGoingToSleepWakeLock.isHeld()) { mGoingToSleepWakeLock.acquire(); if (mLaunchingActivityWakeLock.isHeld()) { - if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) { + if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != SYSTEM_UID) { throw new IllegalStateException("Calling must be system uid"); } mLaunchingActivityWakeLock.release(); @@ -2461,8 +2465,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { case LAUNCH_TIMEOUT_MSG: { if (mLaunchingActivityWakeLock.isHeld()) { Slog.w(TAG, "Launch timeout has expired, giving up wake lock!"); - if (VALIDATE_WAKE_LOCK_CALLER - && Binder.getCallingUid() != Process.myUid()) { + if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != SYSTEM_UID) { throw new IllegalStateException("Calling must be system uid"); } mLaunchingActivityWakeLock.release(); @@ -2623,6 +2626,41 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { } /** + * Fills the info that needs to iterate all activities of task, such as the number of + * non-finishing activities and collecting launch cookies. + */ + static class TaskInfoHelper implements Consumer<ActivityRecord> { + private TaskInfo mInfo; + private ActivityRecord mTopRunning; + + ActivityRecord fillAndReturnTop(Task task, TaskInfo info) { + info.numActivities = 0; + info.baseActivity = null; + mInfo = info; + task.forAllActivities(this); + final ActivityRecord top = mTopRunning; + mTopRunning = null; + mInfo = null; + return top; + } + + @Override + public void accept(ActivityRecord r) { + if (r.finishing) { + return; + } + if (r.mLaunchCookie != null) { + mInfo.addLaunchCookie(r.mLaunchCookie); + } + mInfo.numActivities++; + mInfo.baseActivity = r.mActivityComponent; + if (mTopRunning == null) { + mTopRunning = r; + } + } + } + + /** * Internal container to store a match qualifier alongside a WaitResult. */ private static class WaitInfo { diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java index 6befefda8a12..b23f50154257 100644 --- a/services/core/java/com/android/server/wm/AnrController.java +++ b/services/core/java/com/android/server/wm/AnrController.java @@ -256,7 +256,7 @@ class AnrController { Slog.i(TAG_WM, "Pre-dump for unresponsive"); final ArrayList<Integer> firstPids = new ArrayList<>(1); - firstPids.add(ActivityManagerService.MY_PID); + firstPids.add(WindowManagerService.MY_PID); ArrayList<Integer> nativePids = null; final int[] pids = shouldDumpSf[0] ? Process.getPidsForCommands(new String[] { "/system/bin/surfaceflinger" }) diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 5410dd8508f1..caaaf47e6d11 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -34,6 +34,8 @@ import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH; import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; @@ -64,6 +66,9 @@ import static com.android.internal.R.styleable.WindowAnimation_activityCloseEnte import static com.android.internal.R.styleable.WindowAnimation_activityCloseExitAnimation; import static com.android.internal.R.styleable.WindowAnimation_activityOpenEnterAnimation; import static com.android.internal.R.styleable.WindowAnimation_activityOpenExitAnimation; +import static com.android.internal.R.styleable.WindowAnimation_dreamActivityCloseExitAnimation; +import static com.android.internal.R.styleable.WindowAnimation_dreamActivityOpenEnterAnimation; +import static com.android.internal.R.styleable.WindowAnimation_dreamActivityOpenExitAnimation; import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindSourceAnimation; import static com.android.internal.R.styleable.WindowAnimation_launchTaskBehindTargetAnimation; import static com.android.internal.R.styleable.WindowAnimation_taskCloseEnterAnimation; @@ -645,9 +650,15 @@ public class AppTransition implements Dump { @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction, boolean freeform, WindowContainer container) { - if (mNextAppTransitionOverrideRequested - && (container.canCustomizeAppTransition() || mOverrideTaskTransition)) { - mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM; + final boolean canCustomizeAppTransition = container.canCustomizeAppTransition(); + + if (mNextAppTransitionOverrideRequested) { + if (canCustomizeAppTransition || mOverrideTaskTransition) { + mNextAppTransitionType = NEXT_TRANSIT_TYPE_CUSTOM; + } else { + ProtoLog.e(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: " + + " override requested, but it is prohibited by policy."); + } } Animation a; @@ -835,14 +846,26 @@ public class AppTransition implements Dump { ? WindowAnimation_activityCloseEnterAnimation : WindowAnimation_activityCloseExitAnimation; break; + case TRANSIT_OLD_DREAM_ACTIVITY_OPEN: + animAttr = enter + ? WindowAnimation_dreamActivityOpenEnterAnimation + : WindowAnimation_dreamActivityOpenExitAnimation; + break; + case TRANSIT_OLD_DREAM_ACTIVITY_CLOSE: + animAttr = enter + ? 0 + : WindowAnimation_dreamActivityCloseExitAnimation; + break; } - a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null; + a = animAttr == 0 ? null : (canCustomizeAppTransition + ? loadAnimationAttr(lp, animAttr, transit) + : mTransitionAnimation.loadDefaultAnimationAttr(animAttr, transit)); ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: anim=%s animAttr=0x%x transit=%s isEntrance=%b " - + "Callers=%s", + + " canCustomizeAppTransition=%b Callers=%s", a, animAttr, appTransitionOldToString(transit), enter, - Debug.getCallers(3)); + canCustomizeAppTransition, Debug.getCallers(3)); } setAppTransitionFinishedCallbackIfNeeded(a); @@ -1157,6 +1180,12 @@ public class AppTransition implements Dump { case TRANSIT_OLD_TASK_FRAGMENT_CHANGE: { return "TRANSIT_OLD_TASK_FRAGMENT_CHANGE"; } + case TRANSIT_OLD_DREAM_ACTIVITY_OPEN: { + return "TRANSIT_OLD_DREAM_ACTIVITY_OPEN"; + } + case TRANSIT_OLD_DREAM_ACTIVITY_CLOSE: { + return "TRANSIT_OLD_DREAM_ACTIVITY_CLOSE"; + } default: { return "<UNKNOWN: " + transition + ">"; } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 140ac333e3af..fbb7dac30ef3 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; @@ -32,6 +33,8 @@ import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_RELAUNCH; import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_OCCLUDE; @@ -340,6 +343,9 @@ public class AppTransitionController { ArraySet<WindowContainer> changingContainers, @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper, boolean skipAppTransitionAnimation) { + final ActivityRecord topOpeningApp = getTopApp(openingApps, false /* ignoreHidden */); + final ActivityRecord topClosingApp = getTopApp(closingApps, true /* ignoreHidden */); + // Determine if closing and opening app token sets are wallpaper targets, in which case // special animations are needed. final boolean openingAppHasWallpaper = canBeWallpaperTarget(openingApps) @@ -347,7 +353,7 @@ public class AppTransitionController { final boolean closingAppHasWallpaper = canBeWallpaperTarget(closingApps) && wallpaperTarget != null; - // Keyguard transit has highest priority. + // Keyguard transit has high priority. switch (appTransition.getKeyguardTransition()) { case TRANSIT_KEYGUARD_GOING_AWAY: return openingAppHasWallpaper ? TRANSIT_OLD_KEYGUARD_GOING_AWAY_ON_WALLPAPER @@ -362,6 +368,15 @@ public class AppTransitionController { return TRANSIT_OLD_KEYGUARD_UNOCCLUDE; } + // Determine whether the top opening and closing activity is a dream activity. If so, this + // has higher priority than others except keyguard transit. + if (topOpeningApp != null && topOpeningApp.getActivityType() == ACTIVITY_TYPE_DREAM) { + return TRANSIT_OLD_DREAM_ACTIVITY_OPEN; + } else if (topClosingApp != null + && topClosingApp.getActivityType() == ACTIVITY_TYPE_DREAM) { + return TRANSIT_OLD_DREAM_ACTIVITY_CLOSE; + } + // This is not keyguard transition and one of the app has request to skip app transition. if (skipAppTransitionAnimation) { return WindowManager.TRANSIT_OLD_UNSET; @@ -425,11 +440,6 @@ public class AppTransitionController { } } - final ActivityRecord topOpeningApp = getTopApp(openingApps, - false /* ignoreHidden */); - final ActivityRecord topClosingApp = getTopApp(closingApps, - true /* ignoreHidden */); - if (closingAppHasWallpaper && openingAppHasWallpaper) { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "Wallpaper animation!"); switch (firstTransit) { diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index 61deb59084c5..b519dadb2031 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -144,8 +144,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume // Legacy animation doesn't need to wait for the start transaction. if (mTransitionOp == OP_LEGACY) { mIsStartTransactionCommitted = true; - } else if (displayContent.mTransitionController.useShellTransitionsRotation() - || displayContent.mTransitionController.isCollecting(displayContent)) { + } else if (displayContent.mTransitionController.isCollecting(displayContent)) { keepAppearanceInPreviousRotation(); } } @@ -214,10 +213,10 @@ class AsyncRotationController extends FadeAnimationController implements Consume private void finishOp(WindowToken windowToken) { final Operation op = mTargetWindowTokens.remove(windowToken); if (op == null) return; - if (op.mCapturedDrawTransaction != null) { + if (op.mDrawTransaction != null) { // Unblock the window to show its latest content. - mDisplayContent.getPendingTransaction().merge(op.mCapturedDrawTransaction); - op.mCapturedDrawTransaction = null; + mDisplayContent.getPendingTransaction().merge(op.mDrawTransaction); + op.mDrawTransaction = null; if (DEBUG) Slog.d(TAG, "finishOp merge transaction " + windowToken.getTopChild()); } if (op.mAction == Operation.ACTION_FADE) { @@ -351,14 +350,34 @@ class AsyncRotationController extends FadeAnimationController implements Consume } /** - * Whether the insets animation leash should use previous position when running fade out - * animation in rotated display. + * Whether the insets animation leash should use previous position when running fade animation + * or seamless transformation in a rotated display. */ boolean shouldFreezeInsetsPosition(WindowState w) { return mTransitionOp == OP_APP_SWITCH && w.mTransitionController.inTransition() && isTargetToken(w.mToken); } + /** + * Returns the transaction which will be applied after the window redraws in new rotation. + * This is used to update the position of insets animation leash synchronously. + */ + SurfaceControl.Transaction getDrawTransaction(WindowToken token) { + if (mTransitionOp == OP_LEGACY) { + // Legacy transition uses startSeamlessRotation and finishSeamlessRotation of + // InsetsSourceProvider. + return null; + } + final Operation op = mTargetWindowTokens.get(token); + if (op != null) { + if (op.mDrawTransaction == null) { + op.mDrawTransaction = new SurfaceControl.Transaction(); + } + return op.mDrawTransaction; + } + return null; + } + void setOnShowRunnable(Runnable onShowRunnable) { mOnShowRunnable = onShowRunnable; } @@ -463,10 +482,10 @@ class AsyncRotationController extends FadeAnimationController implements Consume final boolean keepUntilStartTransaction = !mIsStartTransactionCommitted && op.mAction == Operation.ACTION_SEAMLESS; if (!keepUntilTransitionFinish && !keepUntilStartTransaction) return false; - if (op.mCapturedDrawTransaction == null) { - op.mCapturedDrawTransaction = postDrawTransaction; + if (op.mDrawTransaction == null) { + op.mDrawTransaction = postDrawTransaction; } else { - op.mCapturedDrawTransaction.merge(postDrawTransaction); + op.mDrawTransaction.merge(postDrawTransaction); } if (DEBUG) Slog.d(TAG, "Capture draw transaction " + w); return true; @@ -512,7 +531,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume * the start transaction of transition, so there won't be a flickering such as the window * has redrawn during fading out. */ - SurfaceControl.Transaction mCapturedDrawTransaction; + SurfaceControl.Transaction mDrawTransaction; Operation(@Action int action) { mAction = action; diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java index 46ce43335f01..9a94a4f54b61 100644 --- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java +++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java @@ -236,12 +236,18 @@ class BLASTSyncEngine { private void onTimeout() { if (!mActiveSyncs.contains(mSyncId)) return; + boolean allFinished = true; for (int i = mRootMembers.size() - 1; i >= 0; --i) { final WindowContainer<?> wc = mRootMembers.valueAt(i); if (!wc.isSyncFinished()) { + allFinished = false; Slog.i(TAG, "Unfinished container: " + wc); } } + if (allFinished && !mReady) { + Slog.w(TAG, "Sync group " + mSyncId + " timed-out because not ready. If you see " + + "this, please file a bug."); + } finishNow(); } } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index fcdf175cb809..0c6cea82e00c 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -486,14 +486,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { return WindowConfiguration.inMultiWindowMode(windowingMode); } - /** - * Returns true if this container supports split-screen multi-window and can be put in - * split-screen based on its current state. - */ - public boolean supportsSplitScreenWindowingMode() { - return mFullConfiguration.windowConfiguration.supportSplitScreenWindowingMode(); - } - public boolean inPinnedWindowingMode() { return mFullConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED; } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a059ac613dde..a870ed3faa28 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -558,7 +558,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final FixedRotationTransitionListener mFixedRotationTransitionListener = new FixedRotationTransitionListener(); - private PhysicalDisplaySwitchTransitionLauncher mDisplaySwitchTransitionLauncher; + private final PhysicalDisplaySwitchTransitionLauncher mDisplaySwitchTransitionLauncher; + final RemoteDisplayChangeController mRemoteDisplayChangeController; /** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */ final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>(); @@ -1054,6 +1055,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this); mDisplaySwitchTransitionLauncher = new PhysicalDisplaySwitchTransitionLauncher(this, mTransitionController); + mRemoteDisplayChangeController = new RemoteDisplayChangeController(mWmService, mDisplayId); final InputChannel inputChannel = mWmService.mInputManager.monitorInput( "PointerEventDispatcher" + mDisplayId, mDisplayId); @@ -1435,7 +1437,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (!isReady()) { return; } - if (mDisplayRotation.isWaitingForRemoteRotation()) { + if (mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange()) { return; } @@ -1535,8 +1537,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp config = new Configuration(); computeScreenConfiguration(config); } else if (!(mTransitionController.isCollecting(this) - // If waiting for a remote rotation, don't prematurely update configuration. - || mDisplayRotation.isWaitingForRemoteRotation())) { + // If waiting for a remote display change, don't prematurely update configuration. + || mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange())) { // No obvious action we need to take, but if our current state mismatches the // activity manager's, update it, disregarding font scale, which should remain set // to the value of the previous configuration. @@ -1583,8 +1585,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mAtmService.getTaskChangeNotificationController() .notifyTaskRequestedOrientationChanged(task.mTaskId, orientation); } - // Currently there is no use case from non-activity. - if (handleTopActivityLaunchingInDifferentOrientation(r, true /* checkOpening */)) { + // The orientation source may not be the top if it uses SCREEN_ORIENTATION_BEHIND. + final ActivityRecord topCandidate = !r.mVisibleRequested ? topRunningActivity() : r; + if (handleTopActivityLaunchingInDifferentOrientation( + topCandidate, r, true /* checkOpening */)) { // Display orientation should be deferred until the top fixed rotation is finished. return false; } @@ -1596,12 +1600,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp boolean isSyncFinished() { // Do not consider children because if they are requested to be synced, they should be // added to sync group explicitly. - return !mDisplayRotation.isWaitingForRemoteRotation(); + return !mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange(); } /** * Returns a valid rotation if the activity can use different orientation than the display. - * Otherwise {@link #ROTATION_UNDEFINED}. + * Otherwise {@link android.app.WindowConfiguration#ROTATION_UNDEFINED}. */ @Rotation int rotationForActivityInDifferentOrientation(@NonNull ActivityRecord r) { @@ -1611,6 +1615,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM) { return ROTATION_UNDEFINED; } + if (r.mOrientation == ActivityInfo.SCREEN_ORIENTATION_BEHIND) { + final ActivityRecord nextCandidate = getActivity( + a -> a.mOrientation != SCREEN_ORIENTATION_UNSET + && a.mOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND, + r, false /* includeBoundary */, true /* traverseTopToBottom */); + if (nextCandidate != null) { + r = nextCandidate; + } + } if (r.inMultiWindowMode() || r.getRequestedConfigurationOrientation(true /* forDisplay */) == getConfiguration().orientation) { return ROTATION_UNDEFINED; @@ -1624,18 +1637,25 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return rotation; } + boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r, + boolean checkOpening) { + return handleTopActivityLaunchingInDifferentOrientation(r, r, checkOpening); + } + /** * We need to keep display rotation fixed for a while when the activity in different orientation * is launching until the launch animation is done to avoid showing the previous activity * inadvertently in a wrong orientation. * * @param r The launching activity which may change display orientation. + * @param orientationSrc It may be different from {@param r} if the launching activity uses + * "behind" orientation. * @param checkOpening Whether to check if the activity is animating by transition. Set to * {@code true} if the caller is not sure whether the activity is launching. * @return {@code true} if the fixed rotation is started. */ - boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r, - boolean checkOpening) { + private boolean handleTopActivityLaunchingInDifferentOrientation(@NonNull ActivityRecord r, + @NonNull ActivityRecord orientationSrc, boolean checkOpening) { if (!WindowManagerService.ENABLE_FIXED_ROTATION_TRANSFORM) { return false; } @@ -1684,7 +1704,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // animation is not running (it may be swiping to home). return false; } - final int rotation = rotationForActivityInDifferentOrientation(r); + final int rotation = rotationForActivityInDifferentOrientation(orientationSrc); if (rotation == ROTATION_UNDEFINED) { // The display rotation won't be changed by current top activity. The client side // adjustments of previous rotated activity should be cleared earlier. Otherwise if @@ -1813,8 +1833,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp sendNewConfiguration(); return; } - if (mDisplayRotation.isWaitingForRemoteRotation()) { - // There is pending rotation change to apply. + if (mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange()) { + // There is pending display change to apply. return; } // The orientation of display is not changed. @@ -2015,7 +2035,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp ProtoLog.v(WM_DEBUG_ORIENTATION, "Set mOrientationChanging of %s", w); w.setOrientationChanging(true); } - w.mReportOrientationChanged = true; }, true /* traverseTopToBottom */); for (int i = mWmService.mRotationWatchers.size() - 1; i >= 0; i--) { @@ -2205,9 +2224,9 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp final float density = mDisplayMetrics.density; outConfig.screenWidthDp = (int) (mDisplayPolicy.getConfigDisplayWidth(dw, dh, rotation, - uiMode, displayCutout) / density); + uiMode, displayCutout) / density + 0.5f); outConfig.screenHeightDp = (int) (mDisplayPolicy.getConfigDisplayHeight(dw, dh, rotation, - uiMode, displayCutout) / density); + uiMode, displayCutout) / density + 0.5f); outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale); outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale); @@ -2390,7 +2409,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp sl = reduceConfigLayout(sl, Surface.ROTATION_90, density, unrotDh, unrotDw, uiMode); sl = reduceConfigLayout(sl, Surface.ROTATION_180, density, unrotDw, unrotDh, uiMode); sl = reduceConfigLayout(sl, Surface.ROTATION_270, density, unrotDh, unrotDw, uiMode); - outConfig.smallestScreenWidthDp = (int)(displayInfo.smallestNominalAppWidth / density); + outConfig.smallestScreenWidthDp = + (int) (displayInfo.smallestNominalAppWidth / density + 0.5f); outConfig.screenLayout = sl; } @@ -2412,8 +2432,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp longSize = shortSize; shortSize = tmp; } - longSize = (int)(longSize/density); - shortSize = (int)(shortSize/density); + longSize = (int) (longSize / density + 0.5f); + shortSize = (int) (shortSize / density + 0.5f); return Configuration.reduceScreenLayout(curLayout, longSize, shortSize); } @@ -2758,6 +2778,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp private void updateBaseDisplayMetricsIfNeeded() { // Get real display metrics without overrides from WM. mWmService.mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mDisplayInfo); + final int currentRotation = getRotation(); final int orientation = mDisplayInfo.rotation; final boolean rotated = (orientation == ROTATION_90 || orientation == ROTATION_270); final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth; @@ -2818,7 +2839,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp reconfigureDisplayLocked(); if (physicalDisplayChanged) { - mDisplaySwitchTransitionLauncher.onDisplayUpdated(); + mDisplaySwitchTransitionLauncher.onDisplayUpdated(currentRotation, getRotation(), + getDisplayAreaInfo()); } } } @@ -6081,7 +6103,8 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp rootTask.ensureActivitiesVisible(starting, configChanges, preserveWindows, notifyClients); }); - if (mTransitionController.isCollecting() + if (mTransitionController.useShellTransitionsRotation() + && mTransitionController.isCollecting() && mWallpaperController.getWallpaperTarget() != null) { // Also update wallpapers so that their requestedVisibility immediately reflects // the changes to activity visibility. @@ -6250,7 +6273,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** * Sets if Display APIs should be sandboxed to the activity window bounds. */ - @VisibleForTesting void setSandboxDisplayApis(boolean sandboxDisplayApis) { mSandboxDisplayApis = sandboxDisplayApis; } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 7e91989a9105..8733316aef5a 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -61,6 +61,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_TOAST; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION; import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; +import static android.view.WindowManager.TRANSIT_WAKE; import static android.view.WindowManagerGlobal.ADD_OKAY; import static android.view.WindowManagerPolicyConstants.ACTION_HDMI_PLUGGED; import static android.view.WindowManagerPolicyConstants.ALT_BAR_BOTTOM; @@ -778,7 +779,22 @@ public class DisplayPolicy { } public void setAwake(boolean awake) { + if (awake == mAwake) { + return; + } mAwake = awake; + synchronized (mService.mGlobalLock) { + if (!mDisplayContent.isDefaultDisplay) { + return; + } + if (mAwake) { + // Start a transition for waking. This is needed for showWhenLocked activities. + mDisplayContent.mTransitionController.requestTransitionIfNeeded(TRANSIT_WAKE, + 0 /* flags */, null /* trigger */, mDisplayContent); + } + mService.mAtmService.mKeyguardController.updateDeferWakeTransition( + mAwake /* waiting */); + } } public boolean isAwake() { diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 03e1429f1bf1..a78d25f4e21a 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -51,14 +51,12 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.power.Boost; import android.os.Handler; -import android.os.RemoteException; import android.os.SystemProperties; import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; import android.util.TimeUtils; import android.util.proto.ProtoOutputStream; -import android.view.IDisplayWindowRotationCallback; import android.view.IWindowManager; import android.view.Surface; import android.window.TransitionRequestInfo; @@ -67,7 +65,6 @@ import android.window.WindowContainerTransaction; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; -import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.UiThread; import com.android.server.policy.WindowManagerPolicy; @@ -211,31 +208,6 @@ public class DisplayRotation { private boolean mDemoHdmiRotationLock; private boolean mDemoRotationLock; - private static final int REMOTE_ROTATION_TIMEOUT_MS = 800; - - private boolean mIsWaitingForRemoteRotation = false; - - private final Runnable mDisplayRotationHandlerTimeout = - new Runnable() { - @Override - public void run() { - continueRotation(mRotation, null /* transaction */); - } - }; - - private final IDisplayWindowRotationCallback mRemoteRotationCallback = - new IDisplayWindowRotationCallback.Stub() { - @Override - public void continueRotateDisplay(int targetRotation, - WindowContainerTransaction t) { - synchronized (mService.getWindowManagerLock()) { - mService.mH.sendMessage(PooledLambda.obtainMessage( - DisplayRotation::continueRotation, DisplayRotation.this, - targetRotation, t)); - } - } - }; - DisplayRotation(WindowManagerService service, DisplayContent displayContent) { this(service, displayContent, displayContent.getDisplayPolicy(), service.mDisplayWindowSettings, service.mContext, service.getWindowManagerLock()); @@ -511,6 +483,7 @@ public class DisplayRotation { final TransitionRequestInfo.DisplayChange change = wasCollecting ? null : new TransitionRequestInfo.DisplayChange(mDisplayContent.getDisplayId(), oldRotation, mRotation); + mDisplayContent.requestChangeTransitionIfNeeded( ActivityInfo.CONFIG_WINDOW_CONFIGURATION, change); if (wasCollecting) { @@ -554,61 +527,39 @@ public class DisplayRotation { return null; } - /** - * A Remote rotation is when we are waiting for some registered (remote) - * {@link IDisplayWindowRotationController} to calculate and return some hierarchy operations - * to perform in sync with the rotation. - */ - boolean isWaitingForRemoteRotation() { - return mIsWaitingForRemoteRotation; - } - private void startRemoteRotation(int fromRotation, int toRotation) { - if (mService.mDisplayRotationController == null) { - return; - } - mIsWaitingForRemoteRotation = true; - try { - mService.mDisplayRotationController.onRotateDisplay(mDisplayContent.getDisplayId(), - fromRotation, toRotation, mRemoteRotationCallback); - mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout); - mService.mH.postDelayed(mDisplayRotationHandlerTimeout, REMOTE_ROTATION_TIMEOUT_MS); - } catch (RemoteException e) { - mIsWaitingForRemoteRotation = false; - return; - } + mDisplayContent.mRemoteDisplayChangeController.performRemoteDisplayChange( + fromRotation, toRotation, null /* newDisplayAreaInfo */, + (transaction) -> continueRotation(toRotation, transaction) + ); } private void continueRotation(int targetRotation, WindowContainerTransaction t) { - synchronized (mService.mGlobalLock) { - if (targetRotation != mRotation || !mIsWaitingForRemoteRotation) { - // Drop it, this is either coming from an outdated remote rotation; or, we've - // already moved on. - return; - } - mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout); - mIsWaitingForRemoteRotation = false; + if (targetRotation != mRotation) { + // Drop it, this is either coming from an outdated remote rotation; or, we've + // already moved on. + return; + } - if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) { - if (!mDisplayContent.mTransitionController.isCollecting()) { - throw new IllegalStateException("Trying to rotate outside a transition"); - } - mDisplayContent.mTransitionController.collect(mDisplayContent); - // Go through all tasks and collect them before the rotation - // TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper - // handling is synchronized. - mDisplayContent.mTransitionController.collectForDisplayChange(mDisplayContent, - null /* use collecting transition */); + if (mDisplayContent.mTransitionController.isShellTransitionsEnabled()) { + if (!mDisplayContent.mTransitionController.isCollecting()) { + throw new IllegalStateException("Trying to rotate outside a transition"); } - mService.mAtmService.deferWindowLayout(); - try { - mDisplayContent.sendNewConfiguration(); - if (t != null) { - mService.mAtmService.mWindowOrganizerController.applyTransaction(t); - } - } finally { - mService.mAtmService.continueWindowLayout(); + mDisplayContent.mTransitionController.collect(mDisplayContent); + // Go through all tasks and collect them before the rotation + // TODO(shell-transitions): move collect() to onConfigurationChange once wallpaper + // handling is synchronized. + mDisplayContent.mTransitionController.collectForDisplayChange(mDisplayContent, + null /* use collecting transition */); + } + mService.mAtmService.deferWindowLayout(); + try { + mDisplayContent.sendNewConfiguration(); + if (t != null) { + mService.mAtmService.mWindowOrganizerController.applyTransaction(t); } + } finally { + mService.mAtmService.continueWindowLayout(); } } @@ -1571,8 +1522,8 @@ public class DisplayRotation { } @Override - public boolean isKeyguardLocked() { - return mService.isKeyguardLocked(); + public boolean isKeyguardShowingAndNotOccluded() { + return mService.isKeyguardShowingAndNotOccluded(); } @Override diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java index c47d778c60bc..73f0d31e1944 100644 --- a/services/core/java/com/android/server/wm/DragState.java +++ b/services/core/java/com/android/server/wm/DragState.java @@ -31,6 +31,8 @@ import static com.android.server.wm.DragDropController.MSG_TEAR_DOWN_DRAG_AND_DR import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; +import static com.android.server.wm.WindowManagerService.MY_PID; +import static com.android.server.wm.WindowManagerService.MY_UID; import android.animation.Animator; import android.animation.PropertyValuesHolder; @@ -45,7 +47,6 @@ import android.os.Binder; import android.os.Build; import android.os.IBinder; import android.os.InputConfig; -import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -207,8 +208,6 @@ class DragState { // Send drag end broadcast if drag start has been sent. if (mDragInProgress) { - final int myPid = Process.myPid(); - if (DEBUG_DRAG) { Slog.d(TAG_WM, "broadcasting DRAG_ENDED"); } @@ -236,7 +235,7 @@ class DragState { } // if the current window is in the same process, // the dispatch has already recycled the event - if (myPid != ws.mSession.mPid) { + if (MY_PID != ws.mSession.mPid) { event.recycle(); } } @@ -320,7 +319,6 @@ class DragState { mData.fixUris(mSourceUserId); } } - final int myPid = Process.myPid(); final IBinder clientToken = touchedWin.mClient.asBinder(); final DragEvent event = obtainDragEvent(DragEvent.ACTION_DROP, x, y, mData, targetInterceptsGlobalDrag(touchedWin), @@ -336,7 +334,7 @@ class DragState { endDragLocked(); return false; } finally { - if (myPid != touchedWin.mSession.mPid) { + if (MY_PID != touchedWin.mSession.mPid) { event.recycle(); } } @@ -364,8 +362,8 @@ class DragState { mDragWindowHandle.token = mClientChannel.getToken(); mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; - mDragWindowHandle.ownerPid = Process.myPid(); - mDragWindowHandle.ownerUid = Process.myUid(); + mDragWindowHandle.ownerPid = MY_PID; + mDragWindowHandle.ownerUid = MY_UID; mDragWindowHandle.scaleFactor = 1.0f; // Keep the default behavior of this window to be focusable, which allows the system @@ -477,7 +475,7 @@ class DragState { Slog.w(TAG_WM, "Unable to drag-start window " + newWin); } finally { // if the callee was local, the dispatch has already recycled the event - if (Process.myPid() != newWin.mSession.mPid) { + if (MY_PID != newWin.mSession.mPid) { event.recycle(); } } diff --git a/services/core/java/com/android/server/wm/InputConsumerImpl.java b/services/core/java/com/android/server/wm/InputConsumerImpl.java index 59be3e05f2c0..39622c1c5aaf 100644 --- a/services/core/java/com/android/server/wm/InputConsumerImpl.java +++ b/services/core/java/com/android/server/wm/InputConsumerImpl.java @@ -23,7 +23,6 @@ import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.InputConfig; -import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.view.InputApplicationHandle; @@ -72,8 +71,8 @@ class InputConsumerImpl implements IBinder.DeathRecipient { mWindowHandle.token = mClientChannel.getToken(); mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; - mWindowHandle.ownerPid = Process.myPid(); - mWindowHandle.ownerUid = Process.myUid(); + mWindowHandle.ownerPid = WindowManagerService.MY_PID; + mWindowHandle.ownerUid = WindowManagerService.MY_UID; mWindowHandle.scaleFactor = 1.0f; mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.TRUSTED_OVERLAY; diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 358e93d89f64..178e299d317a 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -290,7 +290,22 @@ abstract class InsetsSourceProvider { && windowState.mWinAnimator.getShown() && mWindowContainer.okToDisplay()) { windowState.applyWithNextDraw(mSetLeashPositionConsumer); } else { - mSetLeashPositionConsumer.accept(mWindowContainer.getSyncTransaction()); + Transaction t = mWindowContainer.getSyncTransaction(); + if (windowState != null) { + // Make the buffer, token transformation, and leash position to be updated + // together when the window is drawn for new rotation. Otherwise the window + // may be outside the screen by the inconsistent orientations. + final AsyncRotationController rotationController = + mDisplayContent.getAsyncRotationController(); + if (rotationController != null) { + final Transaction drawT = + rotationController.getDrawTransaction(windowState.mToken); + if (drawT != null) { + t = drawT; + } + } + } + mSetLeashPositionConsumer.accept(t); } } if (mServerVisible && !mLastSourceFrame.equals(mSource.getFrame())) { @@ -310,17 +325,15 @@ abstract class InsetsSourceProvider { } private Point getWindowFrameSurfacePosition() { - WindowState win = mWindowContainer.asWindowState(); - if (mControl != null) { - final AsyncRotationController controller = - win.mDisplayContent.getAsyncRotationController(); + final WindowState win = mWindowContainer.asWindowState(); + if (win != null && mControl != null) { + final AsyncRotationController controller = mDisplayContent.getAsyncRotationController(); if (controller != null && controller.shouldFreezeInsetsPosition(win)) { - // Use previous position because the fade-out animation runs in old rotation. + // Use previous position because the window still shows with old rotation. return mControl.getSurfacePosition(); } } - final Rect frame = mWindowContainer.asWindowState() != null - ? mWindowContainer.asWindowState().getFrame() : mWindowContainer.getBounds(); + final Rect frame = win != null ? win.getFrame() : mWindowContainer.getBounds(); final Point position = new Point(); mWindowContainer.transformFrameToSurfacePosition(frame.left, frame.top, position); return position; diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 01c94142f46b..4d971a9f52da 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -72,6 +72,8 @@ class KeyguardController { static final String KEYGUARD_SLEEP_TOKEN_TAG = "keyguard"; + private static final int DEFER_WAKE_TRANSITION_TIMEOUT_MS = 5000; + private final ActivityTaskSupervisor mTaskSupervisor; private WindowManagerService mWindowManager; @@ -79,7 +81,7 @@ class KeyguardController { private final ActivityTaskManagerService mService; private RootWindowContainer mRootWindowContainer; private final ActivityTaskManagerInternal.SleepTokenAcquirer mSleepTokenAcquirer; - + private boolean mWaitingForWakeTransition; KeyguardController(ActivityTaskManagerService service, ActivityTaskSupervisor taskSupervisor) { @@ -171,6 +173,9 @@ class KeyguardController { // Do not reset keyguardChanged status if this is aodChanged. final boolean keyguardChanged = (keyguardShowing != state.mKeyguardShowing) || (state.mKeyguardGoingAway && keyguardShowing && !aodChanged); + if (aodChanged && !aodShowing) { + updateDeferWakeTransition(false /* waiting */); + } if (!keyguardChanged && !aodChanged) { setWakeTransitionReady(); return; @@ -199,10 +204,6 @@ class KeyguardController { state.mKeyguardShowing = keyguardShowing; state.mAodShowing = aodShowing; - if (aodChanged) { - // Ensure the new state takes effect. - mWindowManager.mWindowPlacerLocked.performSurfacePlacement(); - } if (keyguardChanged) { // Irrelevant to AOD. @@ -220,6 +221,10 @@ class KeyguardController { mRootWindowContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS); InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */); setWakeTransitionReady(); + if (aodChanged) { + // Ensure the new state takes effect. + mWindowManager.mWindowPlacerLocked.performSurfacePlacement(); + } } private void setWakeTransitionReady() { @@ -526,6 +531,33 @@ class KeyguardController { } } + private final Runnable mResetWaitTransition = () -> { + synchronized (mWindowManager.mGlobalLock) { + updateDeferWakeTransition(false /* waiting */); + } + }; + + void updateDeferWakeTransition(boolean waiting) { + if (waiting == mWaitingForWakeTransition) { + return; + } + if (!mWindowManager.mAtmService.getTransitionController().isShellTransitionsEnabled()) { + return; + } + // if aod is showing, defer the wake transition until aod state changed. + if (waiting && isAodShowing(DEFAULT_DISPLAY)) { + mWaitingForWakeTransition = true; + mWindowManager.mAtmService.getTransitionController().deferTransitionReady(); + mWindowManager.mH.postDelayed(mResetWaitTransition, DEFER_WAKE_TRANSITION_TIMEOUT_MS); + } else if (!waiting) { + // dismiss the deferring if the aod state change or cancel awake. + mWaitingForWakeTransition = false; + mWindowManager.mAtmService.getTransitionController().continueTransitionReady(); + mWindowManager.mH.removeCallbacks(mResetWaitTransition); + } + } + + /** Represents Keyguard state per individual display. */ private static class KeyguardDisplayState { private final int mDisplayId; diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index 40df02c176e5..b5eff41d4f62 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -25,7 +25,6 @@ import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; import android.os.InputConfig; -import android.os.Process; import android.view.GestureDetector; import android.view.InputChannel; import android.view.InputEvent; @@ -72,7 +71,8 @@ public class Letterbox { private final LetterboxSurface mFullWindowSurface = new LetterboxSurface("fullWindow"); private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom }; // Reachability gestures. - private final IntConsumer mDoubleTapCallback; + private final IntConsumer mDoubleTapCallbackX; + private final IntConsumer mDoubleTapCallbackY; /** * Constructs a Letterbox. @@ -86,7 +86,8 @@ public class Letterbox { Supplier<Boolean> hasWallpaperBackgroundSupplier, Supplier<Integer> blurRadiusSupplier, Supplier<Float> darkScrimAlphaSupplier, - IntConsumer doubleTapCallback) { + IntConsumer doubleTapCallbackX, + IntConsumer doubleTapCallbackY) { mSurfaceControlFactory = surfaceControlFactory; mTransactionFactory = transactionFactory; mAreCornersRounded = areCornersRounded; @@ -94,7 +95,8 @@ public class Letterbox { mHasWallpaperBackgroundSupplier = hasWallpaperBackgroundSupplier; mBlurRadiusSupplier = blurRadiusSupplier; mDarkScrimAlphaSupplier = darkScrimAlphaSupplier; - mDoubleTapCallback = doubleTapCallback; + mDoubleTapCallbackX = doubleTapCallbackX; + mDoubleTapCallbackY = doubleTapCallbackY; } /** @@ -264,7 +266,8 @@ public class Letterbox { @Override public boolean onDoubleTapEvent(MotionEvent e) { if (e.getAction() == MotionEvent.ACTION_UP) { - mDoubleTapCallback.accept((int) e.getX()); + mDoubleTapCallbackX.accept((int) e.getX()); + mDoubleTapCallbackY.accept((int) e.getY()); return true; } return false; @@ -293,8 +296,8 @@ public class Letterbox { mWindowHandle.token = mToken; mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_INPUT_CONSUMER; mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; - mWindowHandle.ownerPid = Process.myPid(); - mWindowHandle.ownerUid = Process.myUid(); + mWindowHandle.ownerPid = WindowManagerService.MY_PID; + mWindowHandle.ownerUid = WindowManagerService.MY_UID; mWindowHandle.scaleFactor = 1.0f; mWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE | InputConfig.SLIPPERY; } diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index d02ad992c7e8..2d227b66b3ce 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -22,7 +22,6 @@ import android.content.Context; import android.graphics.Color; import com.android.internal.R; -import com.android.internal.annotations.VisibleForTesting; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -38,6 +37,11 @@ final class LetterboxConfiguration { */ static final float MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO = 1.0f; + // Min allowed aspect ratio for unresizable apps which is used when an app doesn't specify + // android:minAspectRatio in accordance with the CDD 7.1.1.2 requirement: + // https://source.android.com/compatibility/12/android-12-cdd#7112_screen_aspect_ratio + static final float MIN_UNRESIZABLE_ASPECT_RATIO = 4 / 3f; + /** Enum for Letterbox background type. */ @Retention(RetentionPolicy.SOURCE) @IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND, @@ -56,25 +60,48 @@ final class LetterboxConfiguration { static final int LETTERBOX_BACKGROUND_WALLPAPER = 3; /** - * Enum for Letterbox reachability position types. + * Enum for Letterbox horizontal reachability position types. * * <p>Order from left to right is important since it's used in {@link * #movePositionForReachabilityToNextRightStop} and {@link * #movePositionForReachabilityToNextLeftStop}. */ @Retention(RetentionPolicy.SOURCE) - @IntDef({LETTERBOX_REACHABILITY_POSITION_LEFT, LETTERBOX_REACHABILITY_POSITION_CENTER, - LETTERBOX_REACHABILITY_POSITION_RIGHT}) - @interface LetterboxReachabilityPosition {}; + @IntDef({LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT, + LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER, + LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT}) + @interface LetterboxHorizontalReachabilityPosition {}; /** Letterboxed app window is aligned to the left side. */ - static final int LETTERBOX_REACHABILITY_POSITION_LEFT = 0; + static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT = 0; /** Letterboxed app window is positioned in the horizontal center. */ - static final int LETTERBOX_REACHABILITY_POSITION_CENTER = 1; + static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER = 1; + + /** Letterboxed app window is aligned to the right side. */ + static final int LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT = 2; + + /** + * Enum for Letterbox vertical reachability position types. + * + * <p>Order from top to bottom is important since it's used in {@link + * #movePositionForReachabilityToNextBottomStop} and {@link + * #movePositionForReachabilityToNextTopStop}. + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP, + LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER, + LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM}) + @interface LetterboxVerticalReachabilityPosition {}; + + /** Letterboxed app window is aligned to the left side. */ + static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP = 0; + + /** Letterboxed app window is positioned in the vertical center. */ + static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER = 1; /** Letterboxed app window is aligned to the right side. */ - static final int LETTERBOX_REACHABILITY_POSITION_RIGHT = 2; + static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM = 2; final Context mContext; @@ -82,6 +109,11 @@ final class LetterboxConfiguration { // MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO will be ignored. private float mFixedOrientationLetterboxAspectRatio; + // Default min aspect ratio for unresizable apps which is used when an app doesn't specify + // android:minAspectRatio in accordance with the CDD 7.1.1.2 requirement: + // https://source.android.com/compatibility/12/android-12-cdd#7112_screen_aspect_ratio + private float mDefaultMinAspectRatioForUnresizableApps; + // Corners radius for activities presented in the letterbox mode, values < 0 will be ignored. private int mLetterboxActivityCornersRadius; @@ -107,29 +139,57 @@ final class LetterboxConfiguration { // side of the screen and 1.0 to the right side. private float mLetterboxHorizontalPositionMultiplier; - // Default horizontal position the letterboxed app window when reachability is enabled and - // an app is fullscreen in landscape device orientatio. - // It is used as a starting point for mLetterboxPositionForReachability. - @LetterboxReachabilityPosition - private int mDefaultPositionForReachability; + // Vertical position of a center of the letterboxed app window. 0 corresponds to the top + // side of the screen and 1.0 to the bottom side. + private float mLetterboxVerticalPositionMultiplier; + + // Default horizontal position the letterboxed app window when horizontal reachability is + // enabled and an app is fullscreen in landscape device orientation. + // It is used as a starting point for mLetterboxPositionForHorizontalReachability. + @LetterboxHorizontalReachabilityPosition + private int mDefaultPositionForHorizontalReachability; + + // Default vertical position the letterboxed app window when vertical reachability is enabled + // and an app is fullscreen in portrait device orientation. + // It is used as a starting point for mLetterboxPositionForVerticalReachability. + @LetterboxVerticalReachabilityPosition + private int mDefaultPositionForVerticalReachability; + + // Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps in + // landscape device orientation. + private boolean mIsHorizontalReachabilityEnabled; + + // Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps in + // portrait device orientation. + private boolean mIsVerticalReachabilityEnabled; - // Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape - // device orientation. - private boolean mIsReachabilityEnabled; // Horizontal position of a center of the letterboxed app window which is global to prevent // "jumps" when switching between letterboxed apps. It's updated to reposition the app window // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in // LetterboxUiController#getHorizontalPositionMultiplier which is called from - // ActivityRecord#updateResolvedBoundsHorizontalPosition. + // ActivityRecord#updateResolvedBoundsPosition. + // TODO(b/199426138): Global reachability setting causes a jump when resuming an app from + // Overview after changing position in another app. + @LetterboxHorizontalReachabilityPosition + private volatile int mLetterboxPositionForHorizontalReachability; + + // Vertical position of a center of the letterboxed app window which is global to prevent + // "jumps" when switching between letterboxed apps. It's updated to reposition the app window + // in response to a double tap gesture (see LetterboxUiController#handleDoubleTap). Used in + // LetterboxUiController#getVerticalPositionMultiplier which is called from + // ActivityRecord#updateResolvedBoundsPosition. // TODO(b/199426138): Global reachability setting causes a jump when resuming an app from // Overview after changing position in another app. - @LetterboxReachabilityPosition - private volatile int mLetterboxPositionForReachability; + @LetterboxVerticalReachabilityPosition + private volatile int mLetterboxPositionForVerticalReachability; // Whether education is allowed for letterboxed fullscreen apps. private boolean mIsEducationEnabled; + // Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. + private boolean mIsSplitScreenAspectRatioForUnresizableAppsEnabled; + LetterboxConfiguration(Context systemUiContext) { mContext = systemUiContext; mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( @@ -143,12 +203,24 @@ final class LetterboxConfiguration { R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha); mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat( R.dimen.config_letterboxHorizontalPositionMultiplier); - mIsReachabilityEnabled = mContext.getResources().getBoolean( - R.bool.config_letterboxIsReachabilityEnabled); - mDefaultPositionForReachability = readLetterboxReachabilityPositionFromConfig(mContext); - mLetterboxPositionForReachability = mDefaultPositionForReachability; + mLetterboxVerticalPositionMultiplier = mContext.getResources().getFloat( + R.dimen.config_letterboxVerticalPositionMultiplier); + mIsHorizontalReachabilityEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsHorizontalReachabilityEnabled); + mIsVerticalReachabilityEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsVerticalReachabilityEnabled); + mDefaultPositionForHorizontalReachability = + readLetterboxHorizontalReachabilityPositionFromConfig(mContext); + mDefaultPositionForVerticalReachability = + readLetterboxVerticalReachabilityPositionFromConfig(mContext); + mLetterboxPositionForHorizontalReachability = mDefaultPositionForHorizontalReachability; + mLetterboxPositionForVerticalReachability = mDefaultPositionForVerticalReachability; mIsEducationEnabled = mContext.getResources().getBoolean( R.bool.config_letterboxIsEducationEnabled); + setDefaultMinAspectRatioForUnresizableApps(mContext.getResources().getFloat( + R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps)); + mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled); } /** @@ -157,7 +229,6 @@ final class LetterboxConfiguration { * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio} will be ignored and * the framework implementation will be used to determine the aspect ratio. */ - @VisibleForTesting void setFixedOrientationLetterboxAspectRatio(float aspectRatio) { mFixedOrientationLetterboxAspectRatio = aspectRatio; } @@ -166,7 +237,6 @@ final class LetterboxConfiguration { * Resets the aspect ratio of letterbox for fixed orientation to {@link * com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}. */ - @VisibleForTesting void resetFixedOrientationLetterboxAspectRatio() { mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio); @@ -180,6 +250,62 @@ final class LetterboxConfiguration { } /** + * Resets the min aspect ratio for unresizable apps which is used when an app doesn't specify + * {@code android:minAspectRatio} to {@link + * R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps}. + * + * @throws AssertionError if {@link + * R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps} is < {@link + * #MIN_UNRESIZABLE_ASPECT_RATIO}. + */ + void resetDefaultMinAspectRatioForUnresizableApps() { + setDefaultMinAspectRatioForUnresizableApps(mContext.getResources().getFloat( + R.dimen.config_letterboxDefaultMinAspectRatioForUnresizableApps)); + } + + /** + * Gets the min aspect ratio for unresizable apps which is used when an app doesn't specify + * {@code android:minAspectRatio}. + */ + float getDefaultMinAspectRatioForUnresizableApps() { + return mDefaultMinAspectRatioForUnresizableApps; + } + + /** + * Overrides the min aspect ratio for unresizable apps which is used when an app doesn't + * specify {@code android:minAspectRatio}. + * + * @throws AssertionError if given value is < {@link #MIN_UNRESIZABLE_ASPECT_RATIO}. + */ + void setDefaultMinAspectRatioForUnresizableApps(float aspectRatio) { + if (aspectRatio < MIN_UNRESIZABLE_ASPECT_RATIO) { + throw new AssertionError( + "Unexpected min aspect ratio for unresizable apps, it should be <= " + + MIN_UNRESIZABLE_ASPECT_RATIO + " but was " + aspectRatio); + } + mDefaultMinAspectRatioForUnresizableApps = aspectRatio; + } + + /** + * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0, + * both it and a value of {@link + * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and + * corners of the activity won't be rounded. + */ + void setLetterboxActivityCornersRadius(int cornersRadius) { + mLetterboxActivityCornersRadius = cornersRadius; + } + + /** + * Resets corners raidus for activities presented in the letterbox mode to {@link + * com.android.internal.R.integer.config_letterboxActivityCornersRadius}. + */ + void resetLetterboxActivityCornersRadius() { + mLetterboxActivityCornersRadius = mContext.getResources().getInteger( + com.android.internal.R.integer.config_letterboxActivityCornersRadius); + } + + /** * Whether corners of letterboxed activities are rounded. */ boolean isLetterboxActivityCornersRounded() { @@ -210,6 +336,34 @@ final class LetterboxConfiguration { return Color.valueOf(mContext.getResources().getColor(colorId)); } + + /** + * Sets color of letterbox background which is used when {@link + * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as + * fallback for other backfround types. + */ + void setLetterboxBackgroundColor(Color color) { + mLetterboxBackgroundColorOverride = color; + } + + /** + * Sets color ID of letterbox background which is used when {@link + * #getLetterboxBackgroundType()} is {@link #LETTERBOX_BACKGROUND_SOLID_COLOR} or as + * fallback for other backfround types. + */ + void setLetterboxBackgroundColorResourceId(int colorId) { + mLetterboxBackgroundColorResourceIdOverride = colorId; + } + + /** + * Resets color of letterbox background to {@link + * com.android.internal.R.color.config_letterboxBackgroundColor}. + */ + void resetLetterboxBackgroundColor() { + mLetterboxBackgroundColorOverride = null; + mLetterboxBackgroundColorResourceIdOverride = null; + } + /** * Gets {@link LetterboxBackgroundType} specified in {@link * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command. @@ -219,6 +373,19 @@ final class LetterboxConfiguration { return mLetterboxBackgroundType; } + /** Sets letterbox background type. */ + void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) { + mLetterboxBackgroundType = backgroundType; + } + + /** + * Resets cletterbox background type to {@link + * com.android.internal.R.integer.config_letterboxBackgroundType}. + */ + void resetLetterboxBackgroundType() { + mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); + } + /** Returns a string representing the given {@link LetterboxBackgroundType}. */ static String letterboxBackgroundTypeToString( @LetterboxBackgroundType int backgroundType) { @@ -248,6 +415,27 @@ final class LetterboxConfiguration { } /** + * Overrides alpha of a black scrim shown over wallpaper for {@link + * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}. + * + * <p>If given value is < 0 or >= 1, both it and a value of {@link + * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored + * and 0.0 (transparent) is instead. + */ + void setLetterboxBackgroundWallpaperDarkScrimAlpha(float alpha) { + mLetterboxBackgroundWallpaperDarkScrimAlpha = alpha; + } + + /** + * Resets alpha of a black scrim shown over wallpaper letterbox background to {@link + * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha}. + */ + void resetLetterboxBackgroundWallpaperDarkScrimAlpha() { + mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha); + } + + /** * Gets alpha of a black scrim shown over wallpaper letterbox background. */ float getLetterboxBackgroundWallpaperDarkScrimAlpha() { @@ -255,6 +443,28 @@ final class LetterboxConfiguration { } /** + * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in + * {@link mLetterboxBackgroundType}. + * + * <p> If given value <= 0, both it and a value of {@link + * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored + * and 0 is used instead. + */ + void setLetterboxBackgroundWallpaperBlurRadius(int radius) { + mLetterboxBackgroundWallpaperBlurRadius = radius; + } + + /** + * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link + * mLetterboxBackgroundType} to {@link + * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}. + */ + void resetLetterboxBackgroundWallpaperBlurRadius() { + mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius); + } + + /** * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link * mLetterboxBackgroundType}. */ @@ -275,72 +485,187 @@ final class LetterboxConfiguration { ? 0.5f : mLetterboxHorizontalPositionMultiplier; } + /* + * Gets vertical position of a center of the letterboxed app window specified + * in {@link com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier} + * or via an ADB command. 0 corresponds to the top side of the screen and 1 to the + * bottom side. + */ + float getLetterboxVerticalPositionMultiplier() { + return (mLetterboxVerticalPositionMultiplier < 0.0f + || mLetterboxVerticalPositionMultiplier > 1.0f) + // Default to central position if invalid value is provided. + ? 0.5f : mLetterboxVerticalPositionMultiplier; + } + /** * Overrides horizontal position of a center of the letterboxed app window. If given value < 0 * or > 1, then it and a value of {@link * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier} are ignored and * central position (0.5) is used. */ - @VisibleForTesting void setLetterboxHorizontalPositionMultiplier(float multiplier) { mLetterboxHorizontalPositionMultiplier = multiplier; } /** + * Overrides vertical position of a center of the letterboxed app window. If given value < 0 + * or > 1, then it and a value of {@link + * com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier} are ignored and + * central position (0.5) is used. + */ + void setLetterboxVerticalPositionMultiplier(float multiplier) { + mLetterboxVerticalPositionMultiplier = multiplier; + } + + /** * Resets horizontal position of a center of the letterboxed app window to {@link * com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}. */ - @VisibleForTesting void resetLetterboxHorizontalPositionMultiplier() { mLetterboxHorizontalPositionMultiplier = mContext.getResources().getFloat( com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier); } + /** + * Resets vertical position of a center of the letterboxed app window to {@link + * com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier}. + */ + void resetLetterboxVerticalPositionMultiplier() { + mLetterboxVerticalPositionMultiplier = mContext.getResources().getFloat( + com.android.internal.R.dimen.config_letterboxVerticalPositionMultiplier); + } + + /* + * Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps in + * landscape device orientation. + */ + boolean getIsHorizontalReachabilityEnabled() { + return mIsHorizontalReachabilityEnabled; + } + /* - * Whether reachability repositioning is allowed for letterboxed fullscreen apps in landscape - * device orientation. + * Whether vertical reachability repositioning is allowed for letterboxed fullscreen apps in + * portrait device orientation. */ - boolean getIsReachabilityEnabled() { - return mIsReachabilityEnabled; + boolean getIsVerticalReachabilityEnabled() { + return mIsVerticalReachabilityEnabled; } /** - * Overrides whether reachability repositioning is allowed for letterboxed fullscreen apps in - * landscape device orientation. + * Overrides whether horizontal reachability repositioning is allowed for letterboxed fullscreen + * apps in landscape device orientation. + */ + void setIsHorizontalReachabilityEnabled(boolean enabled) { + mIsHorizontalReachabilityEnabled = enabled; + } + + /** + * Overrides whether vertical reachability repositioning is allowed for letterboxed fullscreen + * apps in portrait device orientation. + */ + void setIsVerticalReachabilityEnabled(boolean enabled) { + mIsVerticalReachabilityEnabled = enabled; + } + + /** + * Resets whether horizontal reachability repositioning is allowed for letterboxed fullscreen + * apps in landscape device orientation to + * {@link R.bool.config_letterboxIsHorizontalReachabilityEnabled}. */ - @VisibleForTesting - void setIsReachabilityEnabled(boolean enabled) { - mIsReachabilityEnabled = enabled; + void resetIsHorizontalReachabilityEnabled() { + mIsHorizontalReachabilityEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsHorizontalReachabilityEnabled); } /** - * Resets whether reachability repositioning is allowed for letterboxed fullscreen apps in - * landscape device orientation to {@link R.bool.config_letterboxIsReachabilityEnabled}. + * Resets whether vertical reachability repositioning is allowed for letterboxed fullscreen apps + * in portrait device orientation to + * {@link R.bool.config_letterboxIsVerticalReachabilityEnabled}. */ - @VisibleForTesting - void resetIsReachabilityEnabled() { - mIsReachabilityEnabled = mContext.getResources().getBoolean( - R.bool.config_letterboxIsReachabilityEnabled); + void resetIsVerticalReachabilityEnabled() { + mIsVerticalReachabilityEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsVerticalReachabilityEnabled); } /* - * Gets default horizontal position of the letterboxed app window when reachability is enabled. - * Specified in {@link R.integer.config_letterboxDefaultPositionForReachability} or via an ADB - * command. + * Gets default horizontal position of the letterboxed app window when horizontal reachability + * is enabled. + * + * <p> Specified in {@link R.integer.config_letterboxDefaultPositionForHorizontalReachability} + * or via an ADB command. + */ + @LetterboxHorizontalReachabilityPosition + int getDefaultPositionForHorizontalReachability() { + return mDefaultPositionForHorizontalReachability; + } + + /* + * Gets default vertical position of the letterboxed app window when vertical reachability is + * enabled. + * + * <p> Specified in {@link R.integer.config_letterboxDefaultPositionForVerticalReachability} or + * via an ADB command. */ - @LetterboxReachabilityPosition - int getDefaultPositionForReachability() { - return mDefaultPositionForReachability; + @LetterboxVerticalReachabilityPosition + int getDefaultPositionForVerticalReachability() { + return mDefaultPositionForVerticalReachability; + } + + /** + * Overrides default horizontal position of the letterboxed app window when horizontal + * reachability is enabled. + */ + void setDefaultPositionForHorizontalReachability( + @LetterboxHorizontalReachabilityPosition int position) { + mDefaultPositionForHorizontalReachability = position; + } + + /** + * Overrides default vertical position of the letterboxed app window when vertical + * reachability is enabled. + */ + void setDefaultPositionForVerticalReachability( + @LetterboxVerticalReachabilityPosition int position) { + mDefaultPositionForVerticalReachability = position; + } + + /** + * Resets default horizontal position of the letterboxed app window when horizontal reachability + * is enabled to {@link R.integer.config_letterboxDefaultPositionForHorizontalReachability}. + */ + void resetDefaultPositionForHorizontalReachability() { + mDefaultPositionForHorizontalReachability = + readLetterboxHorizontalReachabilityPositionFromConfig(mContext); + } + + /** + * Resets default vertical position of the letterboxed app window when vertical reachability + * is enabled to {@link R.integer.config_letterboxDefaultPositionForVerticalReachability}. + */ + void resetDefaultPositionForVerticalReachability() { + mDefaultPositionForVerticalReachability = + readLetterboxVerticalReachabilityPositionFromConfig(mContext); + } + + @LetterboxHorizontalReachabilityPosition + private static int readLetterboxHorizontalReachabilityPositionFromConfig(Context context) { + int position = context.getResources().getInteger( + R.integer.config_letterboxDefaultPositionForHorizontalReachability); + return position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT + || position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER + || position == LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT + ? position : LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER; } - @LetterboxReachabilityPosition - private static int readLetterboxReachabilityPositionFromConfig(Context context) { + @LetterboxVerticalReachabilityPosition + private static int readLetterboxVerticalReachabilityPositionFromConfig(Context context) { int position = context.getResources().getInteger( - R.integer.config_letterboxDefaultPositionForReachability); - return position == LETTERBOX_REACHABILITY_POSITION_LEFT - || position == LETTERBOX_REACHABILITY_POSITION_CENTER - || position == LETTERBOX_REACHABILITY_POSITION_RIGHT - ? position : LETTERBOX_REACHABILITY_POSITION_CENTER; + R.integer.config_letterboxDefaultPositionForVerticalReachability); + return position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP + || position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER + || position == LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM + ? position : LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER; } /* @@ -350,48 +675,108 @@ final class LetterboxConfiguration { * <p>The position multiplier is changed after each double tap in the letterbox area. */ float getHorizontalMultiplierForReachability() { - switch (mLetterboxPositionForReachability) { - case LETTERBOX_REACHABILITY_POSITION_LEFT: + switch (mLetterboxPositionForHorizontalReachability) { + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT: + return 0.0f; + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER: + return 0.5f; + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT: + return 1.0f; + default: + throw new AssertionError( + "Unexpected letterbox position type: " + + mLetterboxPositionForHorizontalReachability); + } + } + /* + * Gets vertical position of a center of the letterboxed app window when reachability + * is enabled specified. 0 corresponds to the top side of the screen and 1 to the bottom side. + * + * <p>The position multiplier is changed after each double tap in the letterbox area. + */ + float getVerticalMultiplierForReachability() { + switch (mLetterboxPositionForVerticalReachability) { + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP: return 0.0f; - case LETTERBOX_REACHABILITY_POSITION_CENTER: + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER: return 0.5f; - case LETTERBOX_REACHABILITY_POSITION_RIGHT: + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM: return 1.0f; default: throw new AssertionError( - "Unexpected letterbox position type: " + mLetterboxPositionForReachability); + "Unexpected letterbox position type: " + + mLetterboxPositionForVerticalReachability); } } - /** Returns a string representing the given {@link LetterboxReachabilityPosition}. */ - static String letterboxReachabilityPositionToString( - @LetterboxReachabilityPosition int position) { + /** Returns a string representing the given {@link LetterboxHorizontalReachabilityPosition}. */ + static String letterboxHorizontalReachabilityPositionToString( + @LetterboxHorizontalReachabilityPosition int position) { switch (position) { - case LETTERBOX_REACHABILITY_POSITION_LEFT: - return "LETTERBOX_REACHABILITY_POSITION_LEFT"; - case LETTERBOX_REACHABILITY_POSITION_CENTER: - return "LETTERBOX_REACHABILITY_POSITION_CENTER"; - case LETTERBOX_REACHABILITY_POSITION_RIGHT: - return "LETTERBOX_REACHABILITY_POSITION_RIGHT"; + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT: + return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT"; + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER: + return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER"; + case LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT: + return "LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT"; default: throw new AssertionError( "Unexpected letterbox position type: " + position); } } + /** Returns a string representing the given {@link LetterboxVerticalReachabilityPosition}. */ + static String letterboxVerticalReachabilityPositionToString( + @LetterboxVerticalReachabilityPosition int position) { + switch (position) { + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP: + return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP"; + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER: + return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER"; + case LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM: + return "LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM"; + default: + throw new AssertionError( + "Unexpected letterbox position type: " + position); + } + } + + /** + * Changes letterbox position for horizontal reachability to the next available one on the + * right side. + */ + void movePositionForHorizontalReachabilityToNextRightStop() { + mLetterboxPositionForHorizontalReachability = Math.min( + mLetterboxPositionForHorizontalReachability + 1, + LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT); + } + /** - * Changes letterbox position for reachability to the next available one on the right side. + * Changes letterbox position for horizontal reachability to the next available one on the left + * side. */ - void movePositionForReachabilityToNextRightStop() { - mLetterboxPositionForReachability = Math.min( - mLetterboxPositionForReachability + 1, LETTERBOX_REACHABILITY_POSITION_RIGHT); + void movePositionForHorizontalReachabilityToNextLeftStop() { + mLetterboxPositionForHorizontalReachability = + Math.max(mLetterboxPositionForHorizontalReachability - 1, 0); } /** - * Changes letterbox position for reachability to the next available one on the left side. + * Changes letterbox position for vertical reachability to the next available one on the bottom + * side. */ - void movePositionForReachabilityToNextLeftStop() { - mLetterboxPositionForReachability = Math.max(mLetterboxPositionForReachability - 1, 0); + void movePositionForVerticalReachabilityToNextBottomStop() { + mLetterboxPositionForVerticalReachability = Math.min( + mLetterboxPositionForVerticalReachability + 1, + LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM); + } + + /** + * Changes letterbox position for vertical reachability to the next available one on the top + * side. + */ + void movePositionForVerticalReachabilityToNextTopStop() { + mLetterboxPositionForVerticalReachability = + Math.max(mLetterboxPositionForVerticalReachability - 1, 0); } /** @@ -404,8 +789,41 @@ final class LetterboxConfiguration { /** * Overrides whether education is allowed for letterboxed fullscreen apps. */ - @VisibleForTesting void setIsEducationEnabled(boolean enabled) { mIsEducationEnabled = enabled; } + + /** + * Resets whether education is allowed for letterboxed fullscreen apps to + * {@link R.bool.config_letterboxIsEducationEnabled}. + */ + void resetIsEducationEnabled() { + mIsEducationEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsEducationEnabled); + } + + /** + * Whether using split screen aspect ratio as a default aspect ratio for unresizable apps. + */ + boolean getIsSplitScreenAspectRatioForUnresizableAppsEnabled() { + return mIsSplitScreenAspectRatioForUnresizableAppsEnabled; + } + + /** + * Overrides whether using split screen aspect ratio as a default aspect ratio for unresizable + * apps. + */ + void setIsSplitScreenAspectRatioForUnresizableAppsEnabled(boolean enabled) { + mIsSplitScreenAspectRatioForUnresizableAppsEnabled = enabled; + } + + /** + * Resets whether using split screen aspect ratio as a default aspect ratio for unresizable + * apps {@link R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled}. + */ + void resetIsSplitScreenAspectRatioForUnresizableAppsEnabled() { + mIsSplitScreenAspectRatioForUnresizableAppsEnabled = mContext.getResources().getBoolean( + R.bool.config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled); + } + } diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index bb15d76c3bac..df9a87ea1ab0 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -163,7 +163,8 @@ final class LetterboxUiController { this::hasWallpaperBackgroudForLetterbox, this::getLetterboxWallpaperBlurRadius, this::getLetterboxWallpaperDarkScrimAlpha, - this::handleDoubleTap); + this::handleHorizontalDoubleTap, + this::handleVerticalDoubleTap); mLetterbox.attachInput(w); } mActivityRecord.getPosition(mTmpPoint); @@ -193,18 +194,27 @@ final class LetterboxUiController { float getHorizontalPositionMultiplier(Configuration parentConfiguration) { // Don't check resolved configuration because it may not be updated yet during // configuration change. - return isReachabilityEnabled(parentConfiguration) + return isHorizontalReachabilityEnabled(parentConfiguration) // Using the last global dynamic position to avoid "jumps" when moving // between apps or activities. ? mLetterboxConfiguration.getHorizontalMultiplierForReachability() : mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier(); } - float getFixedOrientationLetterboxAspectRatio(Configuration parentConfiguration) { - // Don't check resolved windowing mode because it may not be updated yet during + float getVerticalPositionMultiplier(Configuration parentConfiguration) { + // Don't check resolved configuration because it may not be updated yet during // configuration change. - if (!isReachabilityEnabled(parentConfiguration)) { - return mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio(); + return isVerticalReachabilityEnabled(parentConfiguration) + // Using the last global dynamic position to avoid "jumps" when moving + // between apps or activities. + ? mLetterboxConfiguration.getVerticalMultiplierForReachability() + : mLetterboxConfiguration.getLetterboxVerticalPositionMultiplier(); + } + + float getDefaultMinAspectRatioForUnresizableApps() { + if (!mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled() + || mActivityRecord.getDisplayContent() == null) { + return mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps(); } int dividerWindowWidth = @@ -214,10 +224,14 @@ final class LetterboxUiController { int dividerSize = dividerWindowWidth - dividerInsets * 2; // Getting the same aspect ratio that apps get in split screen. - Rect bounds = new Rect(parentConfiguration.windowConfiguration.getAppBounds()); - bounds.inset(dividerSize, /* dy */ 0); - bounds.right = bounds.centerX(); - + Rect bounds = new Rect(mActivityRecord.getDisplayContent().getBounds()); + if (bounds.width() >= bounds.height()) { + bounds.inset(/* dx */ dividerSize, /* dy */ 0); + bounds.right = bounds.centerX(); + } else { + bounds.inset(/* dx */ 0, /* dy */ dividerSize); + bounds.bottom = bounds.centerY(); + } return computeAspectRatio(bounds); } @@ -225,8 +239,8 @@ final class LetterboxUiController { return mActivityRecord.mWmService.mContext.getResources(); } - private void handleDoubleTap(int x) { - if (!isReachabilityEnabled() || mActivityRecord.isInTransition()) { + private void handleHorizontalDoubleTap(int x) { + if (!isHorizontalReachabilityEnabled() || mActivityRecord.isInTransition()) { return; } @@ -237,10 +251,32 @@ final class LetterboxUiController { if (mLetterbox.getInnerFrame().left > x) { // Moving to the next stop on the left side of the app window: right > center > left. - mLetterboxConfiguration.movePositionForReachabilityToNextLeftStop(); + mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextLeftStop(); } else if (mLetterbox.getInnerFrame().right < x) { // Moving to the next stop on the right side of the app window: left > center > right. - mLetterboxConfiguration.movePositionForReachabilityToNextRightStop(); + mLetterboxConfiguration.movePositionForHorizontalReachabilityToNextRightStop(); + } + + // TODO(197549949): Add animation for transition. + mActivityRecord.recomputeConfiguration(); + } + + private void handleVerticalDoubleTap(int y) { + if (!isVerticalReachabilityEnabled() || mActivityRecord.isInTransition()) { + return; + } + + if (mLetterbox.getInnerFrame().top <= y && mLetterbox.getInnerFrame().bottom >= y) { + // Only react to clicks at the top and bottom of the letterboxed app window. + return; + } + + if (mLetterbox.getInnerFrame().top > y) { + // Moving to the next stop on the top side of the app window: bottom > center > top. + mLetterboxConfiguration.movePositionForVerticalReachabilityToNextTopStop(); + } else if (mLetterbox.getInnerFrame().bottom < y) { + // Moving to the next stop on the bottom side of the app window: top > center > bottom. + mLetterboxConfiguration.movePositionForVerticalReachabilityToNextBottomStop(); } // TODO(197549949): Add animation for transition. @@ -248,25 +284,47 @@ final class LetterboxUiController { } /** - * Whether reachability is enabled for an activity in the curren configuration. + * Whether horizontal reachability is enabled for an activity in the current configuration. * * <p>Conditions that needs to be met: * <ul> * <li>Activity is portrait-only. * <li>Fullscreen window in landscape device orientation. - * <li>Reachability is enabled. + * <li>Horizontal Reachability is enabled. + * </ul> + */ + private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) { + return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled() + && parentConfiguration.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_FULLSCREEN + && (parentConfiguration.orientation == ORIENTATION_LANDSCAPE + && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT); + } + + private boolean isHorizontalReachabilityEnabled() { + return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration()); + } + + /** + * Whether vertical reachability is enabled for an activity in the current configuration. + * + * <p>Conditions that needs to be met: + * <ul> + * <li>Activity is landscape-only. + * <li>Fullscreen window in portrait device orientation. + * <li>Vertical Reachability is enabled. * </ul> */ - private boolean isReachabilityEnabled(Configuration parentConfiguration) { - return mLetterboxConfiguration.getIsReachabilityEnabled() + private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) { + return mLetterboxConfiguration.getIsVerticalReachabilityEnabled() && parentConfiguration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FULLSCREEN - && parentConfiguration.orientation == ORIENTATION_LANDSCAPE - && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT; + && (parentConfiguration.orientation == ORIENTATION_PORTRAIT + && mActivityRecord.getRequestedConfigurationOrientation() == ORIENTATION_LANDSCAPE); } - private boolean isReachabilityEnabled() { - return isReachabilityEnabled(mActivityRecord.getParent().getConfiguration()); + private boolean isVerticalReachabilityEnabled() { + return isVerticalReachabilityEnabled(mActivityRecord.getParent().getConfiguration()); } @VisibleForTesting @@ -474,12 +532,19 @@ final class LetterboxUiController { + getLetterboxWallpaperBlurRadius()); } - pw.println(prefix + " isReachabilityEnabled=" + isReachabilityEnabled()); + pw.println(prefix + " isHorizontalReachabilityEnabled=" + + isHorizontalReachabilityEnabled()); + pw.println(prefix + " isVerticalReachabilityEnabled=" + isVerticalReachabilityEnabled()); pw.println(prefix + " letterboxHorizontalPositionMultiplier=" + getHorizontalPositionMultiplier(mActivityRecord.getParent().getConfiguration())); + pw.println(prefix + " letterboxVerticalPositionMultiplier=" + + getVerticalPositionMultiplier(mActivityRecord.getParent().getConfiguration())); pw.println(prefix + " fixedOrientationLetterboxAspectRatio=" - + getFixedOrientationLetterboxAspectRatio( - mActivityRecord.getParent().getConfiguration())); + + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio()); + pw.println(prefix + " defaultMinAspectRatioForUnresizableApps=" + + mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()); + pw.println(prefix + " isSplitScreenAspectRatioForUnresizableAppsEnabled=" + + mLetterboxConfiguration.getIsSplitScreenAspectRatioForUnresizableAppsEnabled()); } /** @@ -496,6 +561,9 @@ final class LetterboxUiController { if (mainWin.isLetterboxedForDisplayCutout()) { return "DISPLAY_CUTOUT"; } + if (mActivityRecord.isAspectRatioApplied()) { + return "ASPECT_RATIO"; + } return "UNKNOWN_REASON"; } diff --git a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java index 9c1d5601dad5..d209f08e6312 100644 --- a/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java +++ b/services/core/java/com/android/server/wm/PhysicalDisplaySwitchTransitionLauncher.java @@ -22,17 +22,21 @@ import static com.android.internal.R.bool.config_unfoldTransitionEnabled; import static com.android.server.wm.ActivityTaskManagerService.POWER_MODE_REASON_CHANGE_DISPLAY; import android.animation.ValueAnimator; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; import android.hardware.devicestate.DeviceStateManager; import android.os.HandlerExecutor; +import android.window.DisplayAreaInfo; import android.window.TransitionRequestInfo; +import android.window.WindowContainerTransaction; public class PhysicalDisplaySwitchTransitionLauncher { private final DisplayContent mDisplayContent; + private final WindowManagerService mService; private final DeviceStateManager mDeviceStateManager; - private final Context mContext; private final TransitionController mTransitionController; private DeviceStateListener mDeviceStateListener; @@ -46,13 +50,13 @@ public class PhysicalDisplaySwitchTransitionLauncher { public PhysicalDisplaySwitchTransitionLauncher(DisplayContent displayContent, TransitionController transitionController) { mDisplayContent = displayContent; - mContext = mDisplayContent.mWmService.mContext; + mService = displayContent.mWmService; mTransitionController = transitionController; - mDeviceStateManager = mContext.getSystemService(DeviceStateManager.class); + mDeviceStateManager = mService.mContext.getSystemService(DeviceStateManager.class); if (mDeviceStateManager != null) { - mDeviceStateListener = new DeviceStateListener(mContext); + mDeviceStateListener = new DeviceStateListener(mService.mContext); mDeviceStateManager .registerCallback(new HandlerExecutor(mDisplayContent.mWmService.mH), mDeviceStateListener); @@ -74,7 +78,7 @@ public class PhysicalDisplaySwitchTransitionLauncher { if (!mDisplayContent.getLastHasContent()) return; boolean shouldRequestUnfoldTransition = !mIsFolded - && mContext.getResources().getBoolean(config_unfoldTransitionEnabled) + && mService.mContext.getResources().getBoolean(config_unfoldTransitionEnabled) && ValueAnimator.areAnimatorsEnabled(); if (!shouldRequestUnfoldTransition) { @@ -102,11 +106,42 @@ public class PhysicalDisplaySwitchTransitionLauncher { } } - public void onDisplayUpdated() { - if (mTransition != null) { - mTransition.setAllReady(); - mTransition = null; + /** + * Called when physical display is getting updated, this could happen e.g. on foldable + * devices when the physical underlying display is replaced. + * + * @param fromRotation rotation before the display change + * @param toRotation rotation after the display change + * @param newDisplayAreaInfo display area info after the display change + */ + public void onDisplayUpdated(int fromRotation, int toRotation, + @NonNull DisplayAreaInfo newDisplayAreaInfo) { + if (mTransition == null) return; + + final boolean started = mDisplayContent.mRemoteDisplayChangeController + .performRemoteDisplayChange(fromRotation, toRotation, newDisplayAreaInfo, + this::continueDisplayUpdate); + + if (!started) { + markTransitionAsReady(); + } + } + + private void continueDisplayUpdate(@Nullable WindowContainerTransaction transaction) { + if (mTransition == null) return; + + if (transaction != null) { + mService.mAtmService.mWindowOrganizerController.applyTransaction(transaction); } + + markTransitionAsReady(); + } + + private void markTransitionAsReady() { + if (mTransition == null) return; + + mTransition.setAllReady(); + mTransition = null; } class DeviceStateListener extends DeviceStateManager.FoldStateListener { diff --git a/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java new file mode 100644 index 000000000000..43baebc7255a --- /dev/null +++ b/services/core/java/com/android/server/wm/RemoteDisplayChangeController.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2022 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.server.wm; + +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.RemoteException; +import android.util.Slog; +import android.view.IDisplayChangeWindowCallback; +import android.window.DisplayAreaInfo; +import android.window.WindowContainerTransaction; + +import com.android.internal.protolog.common.ProtoLog; + +import java.util.ArrayList; +import java.util.List; + +/** + * A helper class, a wrapper around {@link android.view.IDisplayChangeWindowController} to perform + * a synchronous display change in other parts (e.g. in the Shell) and continue the process + * in the system server. It handles timeouts and multiple requests. + * We have an instance of this controller for each display. + */ +public class RemoteDisplayChangeController { + + private static final String TAG = "RemoteDisplayChangeController"; + + private static final int REMOTE_DISPLAY_CHANGE_TIMEOUT_MS = 800; + + private final WindowManagerService mService; + private final int mDisplayId; + + private final Runnable mTimeoutRunnable = this::onContinueTimedOut; + + // all remote changes that haven't finished yet. + private final List<ContinueRemoteDisplayChangeCallback> mCallbacks = new ArrayList<>(); + + public RemoteDisplayChangeController(WindowManagerService service, int displayId) { + mService = service; + mDisplayId = displayId; + } + + /** + * A Remote change is when we are waiting for some registered (remote) + * {@link IDisplayChangeWindowController} to calculate and return some hierarchy operations + * to perform in sync with the display change. + */ + public boolean isWaitingForRemoteDisplayChange() { + return !mCallbacks.isEmpty(); + } + + /** + * Starts remote display change + * @param fromRotation rotation before the change + * @param toRotation rotation after the change + * @param newDisplayAreaInfo display area info after change + * @param callback that will be called after completing remote display change + * @return true if the change successfully started, false otherwise + */ + public boolean performRemoteDisplayChange( + int fromRotation, int toRotation, + @Nullable DisplayAreaInfo newDisplayAreaInfo, + ContinueRemoteDisplayChangeCallback callback) { + if (mService.mDisplayChangeController == null) { + return false; + } + mCallbacks.add(callback); + + if (newDisplayAreaInfo != null) { + ProtoLog.v(WM_DEBUG_CONFIGURATION, + "Starting remote display change: " + + "from [rot = %d], " + + "to [%dx%d, rot = %d]", + fromRotation, + newDisplayAreaInfo.configuration.windowConfiguration + .getMaxBounds().width(), + newDisplayAreaInfo.configuration.windowConfiguration + .getMaxBounds().height(), + toRotation); + } + + final IDisplayChangeWindowCallback remoteCallback = createCallback(callback); + try { + mService.mH.removeCallbacks(mTimeoutRunnable); + mService.mH.postDelayed(mTimeoutRunnable, REMOTE_DISPLAY_CHANGE_TIMEOUT_MS); + mService.mDisplayChangeController.onDisplayChange(mDisplayId, fromRotation, toRotation, + newDisplayAreaInfo, remoteCallback); + return true; + } catch (RemoteException e) { + Slog.e(TAG, "Exception while dispatching remote display-change", e); + mCallbacks.remove(callback); + return false; + } + } + + private void onContinueTimedOut() { + Slog.e(TAG, "RemoteDisplayChange timed-out, UI might get messed-up after this."); + // timed-out, so run all continue callbacks and clear the list + synchronized (mService.mGlobalLock) { + for (int i = 0; i < mCallbacks.size(); ++i) { + mCallbacks.get(i).onContinueRemoteDisplayChange(null /* transaction */); + } + mCallbacks.clear(); + } + } + + private void continueDisplayChange(@NonNull ContinueRemoteDisplayChangeCallback callback, + @Nullable WindowContainerTransaction transaction) { + synchronized (mService.mGlobalLock) { + int idx = mCallbacks.indexOf(callback); + if (idx < 0) { + // already called this callback or a more-recent one (eg. via timeout) + return; + } + for (int i = 0; i < idx; ++i) { + // Expect remote callbacks in order. If they don't come in order, then force + // ordering by continuing everything up until this one with empty transactions. + mCallbacks.get(i).onContinueRemoteDisplayChange(null /* transaction */); + } + mCallbacks.subList(0, idx + 1).clear(); + if (mCallbacks.isEmpty()) { + mService.mH.removeCallbacks(mTimeoutRunnable); + } + callback.onContinueRemoteDisplayChange(transaction); + } + } + + private IDisplayChangeWindowCallback createCallback( + @NonNull ContinueRemoteDisplayChangeCallback callback) { + return new IDisplayChangeWindowCallback.Stub() { + @Override + public void continueDisplayChange(WindowContainerTransaction t) { + synchronized (mService.mGlobalLock) { + if (!mCallbacks.contains(callback)) { + // already ran this callback or a more-recent one. + return; + } + mService.mH.post(() -> RemoteDisplayChangeController.this + .continueDisplayChange(callback, t)); + } + } + }; + } + + /** + * Callback interface to handle continuation of the remote display change + */ + public interface ContinueRemoteDisplayChangeCallback { + /** + * This method is called when the remote display change has been applied + * @param transaction window changes collected by the remote display change + */ + void onContinueRemoteDisplayChange(@Nullable WindowContainerTransaction transaction); + } +} diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ef18b50e910b..326c450f4d5c 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1977,23 +1977,28 @@ class RootWindowContainer extends WindowContainer<DisplayContent> void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, @Nullable ActivityRecord launchIntoPipHostActivity, String reason) { - mService.deferWindowLayout(); + moveActivityToPinnedRootTask(r, launchIntoPipHostActivity, reason, null /* transition */); + } + void moveActivityToPinnedRootTask(@NonNull ActivityRecord r, + @Nullable ActivityRecord launchIntoPipHostActivity, String reason, + @Nullable Transition transition) { final TaskDisplayArea taskDisplayArea = r.getDisplayArea(); + final Task task = r.getTask(); + final Task rootTask; - try { - final Task task = r.getTask(); - - // Create a transition now to collect the current pinned Task dismiss. Only do the - // create here as the Task (trigger) to enter PIP is not ready yet. - final TransitionController transitionController = task.mTransitionController; - Transition newTransition = null; - if (transitionController.isCollecting()) { - transitionController.setReady(task, false /* ready */); - } else if (transitionController.getTransitionPlayer() != null) { - newTransition = transitionController.createTransition(TRANSIT_PIP); - } + Transition newTransition = transition; + // Create a transition now (if not provided) to collect the current pinned Task dismiss. + // Only do the create here as the Task (trigger) to enter PIP is not ready yet. + final TransitionController transitionController = task.mTransitionController; + if (newTransition == null && !transitionController.isCollecting() + && transitionController.getTransitionPlayer() != null) { + newTransition = transitionController.createTransition(TRANSIT_PIP); + } + transitionController.deferTransitionReady(); + mService.deferWindowLayout(); + try { // This will change the root pinned task's windowing mode to its original mode, ensuring // we only have one root task that is in pinned mode. final Task rootPinnedTask = taskDisplayArea.getRootPinnedTask(); @@ -2011,7 +2016,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> final TaskFragment organizedTf = r.getOrganizedTaskFragment(); final boolean singleActivity = task.getNonFinishingActivityCount() == 1; - final Task rootTask; if (singleActivity) { rootTask = task; @@ -2044,6 +2048,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> task.mLastRecentsAnimationTransaction, task.mLastRecentsAnimationOverlay); task.clearLastRecentsAnimationTransaction(false /* forceRemoveOverlay */); + } else { + // Reset the original task surface + task.resetSurfaceControlTransforms(); } // The organized TaskFragment is becoming empty because this activity is reparented @@ -2077,6 +2084,9 @@ class RootWindowContainer extends WindowContainer<DisplayContent> oldTopActivity.mRequestForceTransition = true; } } + + transitionController.collect(rootTask); + // The intermediate windowing mode to be set on the ActivityRecord later. // This needs to happen before the re-parenting, otherwise we will always set the // ActivityRecord to be fullscreen. @@ -2087,13 +2097,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent> rootTask.reparent(taskDisplayArea, true /* onTop */); } - // The new PIP Task is ready, start the transition before updating the windowing mode. - if (newTransition != null) { - transitionController.requestStartTransition(newTransition, rootTask, - null /* remoteTransition */, null /* displayChange */); - } - transitionController.collect(rootTask); - // Defer the windowing mode change until after the transition to prevent the activity // from doing work and changing the activity visuals while animating // TODO(task-org): Figure-out more structured way to do this long term. @@ -2136,9 +2139,23 @@ class RootWindowContainer extends WindowContainer<DisplayContent> } } finally { mService.continueWindowLayout(); + try { + ensureActivitiesVisible(null, 0, false /* preserveWindows */); + } finally { + transitionController.continueTransitionReady(); + } + } + + if (newTransition != null) { + // Request at end since we want task-organizer events from ensureActivitiesVisible + // to be recognized. + transitionController.requestStartTransition(newTransition, rootTask, + null /* remoteTransition */, null /* displayChange */); + // A new transition was created just for this operations. Since the operation is + // complete, mark it as ready. + newTransition.setReady(rootTask, true /* ready */); } - ensureActivitiesVisible(null, 0, false /* preserveWindows */); resumeFocusedTasksTopActivities(); notifyActivityPipModeChanged(r.getTask(), r); diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index fd05f19f54c5..baa31a073dd2 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -173,7 +173,7 @@ public class SafeActivityOptions { if (adapter == null) { return; } - if (callingPid == Process.myPid()) { + if (callingPid == WindowManagerService.MY_PID) { Slog.wtf(TAG, "Safe activity options constructed after clearing calling id"); return; } diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java index e8445ab8c35e..68dbb0607ac1 100644 --- a/services/core/java/com/android/server/wm/StartingSurfaceController.java +++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java @@ -221,6 +221,11 @@ public class StartingSurfaceController { // Attempt to add starting window from the top-most activity. for (int i = mDeferringAddStartActivities.size() - 1; i >= 0; --i) { final DeferringStartingWindowRecord next = mDeferringAddStartActivities.get(i); + if (next.mDeferring.getTask() == null) { + Slog.e(TAG, "No task exists: " + next.mDeferring.shortComponentName + + " parent: " + next.mDeferring.getParent()); + continue; + } next.mDeferring.showStartingWindow(next.mPrev, mInitNewTask, mInitTaskSwitch, mInitProcessRunning, true /* startActivity */, next.mSource, topOptions); // If one succeeds, it is done. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index bf5246f2339a..7c1488b71bf5 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -67,6 +67,7 @@ import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN_BEHIND; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; +import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED; import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP; import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP; @@ -76,7 +77,6 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; -import static com.android.server.wm.ActivityRecord.State.INITIALIZING; import static com.android.server.wm.ActivityRecord.State.PAUSED; import static com.android.server.wm.ActivityRecord.State.PAUSING; import static com.android.server.wm.ActivityRecord.State.RESUMED; @@ -197,6 +197,7 @@ import android.window.WindowContainerToken; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; +import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.XmlUtils; import com.android.internal.util.function.pooled.PooledConsumer; @@ -426,9 +427,6 @@ class Task extends TaskFragment { /** Helper object used for updating override configuration. */ private Configuration mTmpConfig = new Configuration(); - /** Used by fillTaskInfo */ - final TaskActivitiesReport mReuseActivitiesReport = new TaskActivitiesReport(); - /* Unique identifier for this task. */ final int mTaskId; /* User for which this task was created. */ @@ -1404,15 +1402,6 @@ class Task extends TaskFragment { } /** - * Return the number of running activities, and the number of non-finishing/initializing - * activities in the provided {@param reportOut} respectively. - */ - private void getNumRunningActivities(TaskActivitiesReport reportOut) { - reportOut.reset(); - forAllActivities(reportOut); - } - - /** * Reorder the history task so that the passed activity is brought to the front. */ final void moveActivityToFrontLocked(ActivityRecord newTop) { @@ -1707,23 +1696,6 @@ class Task extends TaskFragment { lockTaskAuthToString()); } - @Override - public boolean supportsSplitScreenWindowingMode() { - return supportsSplitScreenWindowingModeInDisplayArea(getDisplayArea()); - } - - boolean supportsSplitScreenWindowingModeInDisplayArea(@Nullable TaskDisplayArea tda) { - final Task topTask = getTopMostTask(); - return super.supportsSplitScreenWindowingMode() - && (topTask == null || topTask.supportsSplitScreenWindowingModeInner(tda)); - } - - private boolean supportsSplitScreenWindowingModeInner(@Nullable TaskDisplayArea tda) { - return super.supportsSplitScreenWindowingMode() - && mAtmService.mSupportsSplitScreenMultiWindow - && supportsMultiWindowInDisplayArea(tda); - } - boolean supportsFreeform() { return supportsFreeformInDisplayArea(getDisplayArea()); } @@ -2031,10 +2003,6 @@ class Task extends TaskFragment { Rect outOverrideBounds = getResolvedOverrideConfiguration().windowConfiguration.getBounds(); if (windowingMode == WINDOWING_MODE_FULLSCREEN) { - if (!isOrganized()) { - // Use empty bounds to indicate "fill parent". - outOverrideBounds.setEmpty(); - } // The bounds for fullscreen mode shouldn't be adjusted by minimal size. Otherwise if // the parent or display is smaller than the size, the content may be cropped. return; @@ -3384,14 +3352,14 @@ class Task extends TaskFragment { * the give {@link TaskDisplayArea}. */ void fillTaskInfo(TaskInfo info, boolean stripExtras, @Nullable TaskDisplayArea tda) { - getNumRunningActivities(mReuseActivitiesReport); + info.launchCookies.clear(); + info.addLaunchCookie(mLaunchCookie); + final ActivityRecord top = mTaskSupervisor.mTaskInfoHelper.fillAndReturnTop(this, info); + info.userId = isLeafTask() ? mUserId : mCurrentUser; info.taskId = mTaskId; info.displayId = getDisplayId(); - if (tda != null) { - info.displayAreaFeatureId = tda.mFeatureId; - } - info.isRunning = getTopNonFinishingActivity() != null; + info.displayAreaFeatureId = tda != null ? tda.mFeatureId : FEATURE_UNDEFINED; final Intent baseIntent = getBaseIntent(); // Make a copy of base intent because this is like a snapshot info. // Besides, {@link RecentTasks#getRecentTasksImpl} may modify it. @@ -3400,18 +3368,13 @@ class Task extends TaskFragment { ? new Intent() : stripExtras ? baseIntent.cloneFilter() : new Intent(baseIntent); info.baseIntent.setFlags(baseIntentFlags); - info.baseActivity = mReuseActivitiesReport.base != null - ? mReuseActivitiesReport.base.intent.getComponent() - : null; - info.topActivity = mReuseActivitiesReport.top != null - ? mReuseActivitiesReport.top.mActivityComponent - : null; + + info.isRunning = top != null; + info.topActivity = top != null ? top.mActivityComponent : null; info.origActivity = origActivity; info.realActivity = realActivity; - info.numActivities = mReuseActivitiesReport.numActivities; info.lastActiveTime = lastActiveTime; info.taskDescription = new ActivityManager.TaskDescription(getTaskDescription()); - info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingModeInDisplayArea(tda); info.supportsMultiWindow = supportsMultiWindowInDisplayArea(tda); info.configuration.setTo(getConfiguration()); // Update to the task's current activity type and windowing mode which may differ from the @@ -3422,49 +3385,38 @@ class Task extends TaskFragment { //TODO (AM refactor): Just use local once updateEffectiveIntent is run during all child // order changes. - final Task top = getTopMostTask(); - info.resizeMode = top != null ? top.mResizeMode : mResizeMode; - info.topActivityType = top.getActivityType(); + final Task topTask = top != null ? top.getTask() : this; + info.resizeMode = topTask.mResizeMode; + info.topActivityType = topTask.getActivityType(); + info.displayCutoutInsets = topTask.getDisplayCutoutInsets(); info.isResizeable = isResizeable(); info.minWidth = mMinWidth; info.minHeight = mMinHeight; info.defaultMinSize = mDisplayContent == null ? DEFAULT_MIN_TASK_SIZE_DP : mDisplayContent.mMinSizeOfResizeableTaskDp; - info.positionInParent = getRelativePosition(); + info.topActivityInfo = top != null ? top.info : null; info.pictureInPictureParams = getPictureInPictureParams(top); - info.shouldDockBigOverlays = shouldDockBigOverlays(); - if (info.pictureInPictureParams != null + info.launchIntoPipHostTaskId = (info.pictureInPictureParams != null && info.pictureInPictureParams.isLaunchIntoPip() - && top.getTopMostActivity().getLastParentBeforePip() != null) { - info.launchIntoPipHostTaskId = - top.getTopMostActivity().getLastParentBeforePip().mTaskId; - } - info.displayCutoutInsets = top != null ? top.getDisplayCutoutInsets() : null; - info.topActivityInfo = mReuseActivitiesReport.top != null - ? mReuseActivitiesReport.top.info - : null; - - boolean isTopActivityResumed = mReuseActivitiesReport.top != null - && mReuseActivitiesReport.top.getOrganizedTask() == this - && mReuseActivitiesReport.top.isState(RESUMED); + && top.getLastParentBeforePip() != null) + ? top.getLastParentBeforePip().mTaskId : INVALID_TASK_ID; + info.shouldDockBigOverlays = top != null && top.shouldDockBigOverlays; + info.mTopActivityLocusId = top != null ? top.getLocusId() : null; + + final boolean isTopActivityResumed = top != null + && top.getOrganizedTask() == this && top.isState(RESUMED); // Whether the direct top activity is in size compat mode on foreground. - info.topActivityInSizeCompat = isTopActivityResumed - && mReuseActivitiesReport.top.inSizeCompatMode(); + info.topActivityInSizeCompat = isTopActivityResumed && top.inSizeCompatMode(); // Whether the direct top activity is eligible for letterbox education. info.topActivityEligibleForLetterboxEducation = isTopActivityResumed - && mReuseActivitiesReport.top.isEligibleForLetterboxEducation(); + && top.isEligibleForLetterboxEducation(); // Whether the direct top activity requested showing camera compat control. info.cameraCompatControlState = isTopActivityResumed - ? mReuseActivitiesReport.top.getCameraCompatControlState() + ? top.getCameraCompatControlState() : TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN; - info.launchCookies.clear(); - info.addLaunchCookie(mLaunchCookie); - forAllActivities(r -> { - info.addLaunchCookie(r.mLaunchCookie); - }); final Task parentTask = getParent() != null ? getParent().asTask() : null; info.parentTaskId = parentTask != null && parentTask.mCreatedByOrganizer ? parentTask.mTaskId @@ -3472,19 +3424,17 @@ class Task extends TaskFragment { info.isFocused = isFocused(); info.isVisible = hasVisibleChildren(); info.isSleeping = shouldSleepActivities(); - ActivityRecord topRecord = getTopNonFinishingActivity(); - info.mTopActivityLocusId = topRecord != null ? topRecord.getLocusId() : null; } @Nullable PictureInPictureParams getPictureInPictureParams() { - return getPictureInPictureParams(getTopMostTask()); + final Task topTask = getTopMostTask(); + if (topTask == null) return null; + return getPictureInPictureParams(topTask.getTopMostActivity()); } - private @Nullable PictureInPictureParams getPictureInPictureParams(Task top) { - if (top == null) return null; - final ActivityRecord topMostActivity = top.getTopMostActivity(); - return (topMostActivity == null || topMostActivity.pictureInPictureArgs.empty()) - ? null : new PictureInPictureParams(topMostActivity.pictureInPictureArgs); + private static @Nullable PictureInPictureParams getPictureInPictureParams(ActivityRecord top) { + return (top == null || top.pictureInPictureArgs.empty()) + ? null : new PictureInPictureParams(top.pictureInPictureArgs); } private boolean shouldDockBigOverlays() { @@ -3718,42 +3668,6 @@ class Task extends TaskFragment { return toString(); } - /** @see #getNumRunningActivities(TaskActivitiesReport) */ - static class TaskActivitiesReport implements Consumer<ActivityRecord> { - int numRunning; - int numActivities; - ActivityRecord top; - ActivityRecord base; - - void reset() { - numRunning = numActivities = 0; - top = base = null; - } - - @Override - public void accept(ActivityRecord r) { - if (r.finishing) { - return; - } - - base = r; - - // Increment the total number of non-finishing activities - numActivities++; - - if (top == null || (top.isState(INITIALIZING))) { - top = r; - // Reset the number of running activities until we hit the first non-initializing - // activity - numRunning = 0; - } - if (r.attachedToProcess()) { - // Increment the number of actually running activities - numRunning++; - } - } - } - /** * Saves this {@link Task} to XML using given serializer. */ @@ -4652,22 +4566,12 @@ class Task extends TaskFragment { moveToFront(reason, null); } - void moveToFront(String reason, Task task) { - if (mMoveAdjacentTogether && getAdjacentTaskFragment() != null) { - final Task adjacentTask = getAdjacentTaskFragment().asTask(); - if (adjacentTask != null) { - adjacentTask.moveToFrontInner(reason + " adjacentTaskToTop", null /* task */); - } - } - moveToFrontInner(reason, task); - } - /** * @param reason The reason for moving the root task to the front. * @param task If non-null, the task will be moved to the top of the root task. */ @VisibleForTesting - void moveToFrontInner(String reason, Task task) { + void moveToFront(String reason, Task task) { if (!isAttached()) { return; } @@ -5581,23 +5485,10 @@ class Task extends TaskFragment { } } - /** - * Worker method for rearranging history task. Implements the function of moving all - * activities for a specific task (gathering them if disjoint) into a single group at the - * bottom of the root task. - * - * If a watcher is installed, the action is preflighted and the watcher has an opportunity - * to premeptively cancel the move. - * - * @param tr The task to collect and move to the bottom. - * @return Returns true if the move completed, false if not. - */ - boolean moveTaskToBack(Task tr) { - Slog.i(TAG, "moveTaskToBack: " + tr); - + private boolean canMoveTaskToBack(Task task) { // In LockTask mode, moving a locked task to the back of the root task may expose unlocked // ones. Therefore we need to check if this operation is allowed. - if (!mAtmService.getLockTaskController().canMoveTaskToBack(tr)) { + if (!mAtmService.getLockTaskController().canMoveTaskToBack(task)) { return false; } @@ -5605,7 +5496,7 @@ class Task extends TaskFragment { // for *other* available tasks, but if none are available, then try again allowing the // current task to be selected. if (isTopRootTaskInDisplayArea() && mAtmService.mController != null) { - ActivityRecord next = topRunningActivity(null, tr.mTaskId); + ActivityRecord next = topRunningActivity(null, task.mTaskId); if (next == null) { next = topRunningActivity(null, INVALID_TASK_ID); } @@ -5623,15 +5514,70 @@ class Task extends TaskFragment { } } } + return true; + } + + /** + * Worker method for rearranging history task. Implements the function of moving all + * activities for a specific task (gathering them if disjoint) into a single group at the + * bottom of the root task. + * + * If a watcher is installed, the action is preflighted and the watcher has an opportunity + * to premeptively cancel the move. + * + * If this is a pinned task, it will be removed instead of rearranged. + * + * @param tr The task to collect and move to the bottom. + * @return Returns true if the move completed, false if not. + */ + boolean moveTaskToBack(Task tr) { + Slog.i(TAG, "moveTaskToBack: " + tr); + + if (!canMoveTaskToBack(tr)) return false; if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to back transition: task=" + tr.mTaskId); - // Skip the transition for pinned task. - if (!inPinnedWindowingMode()) { - mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_TO_BACK, tr); + if (mTransitionController.isShellTransitionsEnabled()) { + final Transition transition = new Transition(TRANSIT_TO_BACK, 0 /* flags */, + mTransitionController, mWmService.mSyncEngine); + // Guarantee that this gets its own transition by queueing on SyncEngine + if (mWmService.mSyncEngine.hasActiveSync()) { + ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, + "Creating Pending Move-to-back: %s", transition); + mWmService.mSyncEngine.queueSyncSet( + () -> mTransitionController.moveToCollecting(transition), + () -> { + mTransitionController.requestStartTransition(transition, tr, + null /* remoteTransition */, null /* displayChange */); + // Need to check again since this happens later and the system might + // be in a different state. + if (!canMoveTaskToBack(tr)) { + Slog.e(TAG, "Failed to move task to back after saying we could: " + + tr.mTaskId); + transition.abort(); + return; + } + moveTaskToBackInner(tr); + }); + } else { + mTransitionController.moveToCollecting(transition); + mTransitionController.requestStartTransition(transition, tr, + null /* remoteTransition */, null /* displayChange */); + moveTaskToBackInner(tr); + } + } else { + // Skip the transition for pinned task. + if (!inPinnedWindowingMode()) { + mDisplayContent.prepareAppTransition(TRANSIT_TO_BACK); + } + moveTaskToBackInner(tr); } - moveToBack("moveTaskToBackLocked", tr); + return true; + } + + private boolean moveTaskToBackInner(@NonNull Task task) { + moveToBack("moveTaskToBackInner", task); if (inPinnedWindowingMode()) { mTaskSupervisor.removeRootTask(this); @@ -6028,8 +5974,13 @@ class Task extends TaskFragment { mLastRecentsAnimationTransaction = null; mLastRecentsAnimationOverlay = null; // reset also the crop and transform introduced by mLastRecentsAnimationTransaction - getPendingTransaction().setMatrix(mSurfaceControl, Matrix.IDENTITY_MATRIX, new float[9]) + resetSurfaceControlTransforms(); + } + + void resetSurfaceControlTransforms() { + getSyncTransaction().setMatrix(mSurfaceControl, Matrix.IDENTITY_MATRIX, new float[9]) .setWindowCrop(mSurfaceControl, null) + .setShadowRadius(mSurfaceControl, 0) .setCornerRadius(mSurfaceControl, 0); } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index dd1b50fc5e0b..d9835722130f 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -29,7 +29,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; -import static android.view.WindowManagerPolicyConstants.SPLIT_DIVIDER_LAYER; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ActivityRecord.State.RESUMED; @@ -91,20 +90,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { */ private int mColorLayerCounter = 0; - /** - * Given that the split-screen divider does not have an AppWindowToken, it - * will have to live inside of a "NonAppWindowContainer". However, in visual Z order - * it will need to be interleaved with some of our children, appearing on top of - * both docked root tasks but underneath any assistant root tasks. - * - * To solve this problem we have this anchor control, which will always exist so - * we can always assign it the correct value in our {@link #assignChildLayers}. - * Likewise since it always exists, we can always - * assign the divider a layer relative to it. This way we prevent linking lifecycle - * events between tasks and the divider window. - */ - private SurfaceControl mSplitScreenDividerAnchor; - // Cached reference to some special tasks we tend to get a lot so we don't need to loop // through the list to find them. private Task mRootHomeTask; @@ -422,8 +407,10 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // wasContained} restricts the preferred root task is set only when moving an existing // root task to top instead of adding a new root task that may be too early (e.g. in the // middle of launching or reparenting). - if (moveToTop && child.isFocusableAndVisible()) { - mPreferredTopFocusableRootTask = child; + final boolean isTopFocusableTask = moveToTop && child.isTopActivityFocusable(); + if (isTopFocusableTask) { + mPreferredTopFocusableRootTask = + child.shouldBeVisible(null /* starting */) ? child : null; } else if (mPreferredTopFocusableRootTask == child) { mPreferredTopFocusableRootTask = null; } @@ -728,12 +715,7 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { // Place root home tasks to the bottom. layer = adjustRootTaskLayer(t, mTmpHomeChildren, layer); layer = adjustRootTaskLayer(t, mTmpNormalChildren, layer); - // TODO(b/207185041): Remove this divider workaround after we full remove leagacy split and - // make app pair split only have single root then we can just attach the - // divider to the single root task in shell. - layer = Math.max(layer, SPLIT_DIVIDER_LAYER + 1); adjustRootTaskLayer(t, mTmpAlwaysOnTopChildren, layer); - t.setLayer(mSplitScreenDividerAnchor, SPLIT_DIVIDER_LAYER); } /** @@ -761,19 +743,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { continue; } - final Task childTask = child.asTask(); - final boolean inAdjacentTask = childTask != null - && child.inMultiWindowMode() - && childTask.getRootTask().getAdjacentTaskFragment() != null; - - if (inAdjacentTask) { - hasAdjacentTask = true; - } else if (hasAdjacentTask && startLayer < SPLIT_DIVIDER_LAYER) { - // Task on top of adjacent tasks should be higher than split divider layer so - // set it as start. - startLayer = SPLIT_DIVIDER_LAYER + 1; - } - child.assignLayer(t, startLayer++); } @@ -800,31 +769,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { return activity != null ? activity.createRemoteAnimationTarget(record) : null; } - SurfaceControl getSplitScreenDividerAnchor() { - return mSplitScreenDividerAnchor; - } - - @Override - void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) { - if (getParent() != null) { - super.onParentChanged(newParent, oldParent, () -> { - mSplitScreenDividerAnchor = makeChildSurface(null) - .setName("splitScreenDividerAnchor") - .setCallsite("TaskDisplayArea.onParentChanged") - .build(); - - getSyncTransaction() - .show(mSplitScreenDividerAnchor); - }); - } else { - super.onParentChanged(newParent, oldParent); - mWmService.mTransactionFactory.get() - .remove(mSplitScreenDividerAnchor) - .apply(); - mSplitScreenDividerAnchor = null; - } - } - void setBackgroundColor(@ColorInt int colorInt) { setBackgroundColor(colorInt, false /* restore */); } @@ -870,12 +814,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { setBackgroundColor(mBackgroundColor, true /* restore */); } - if (mSplitScreenDividerAnchor == null) { - return; - } - - // As TaskDisplayArea is getting a new surface, reparent and reorder the child surfaces. - t.reparent(mSplitScreenDividerAnchor, mSurfaceControl); reassignLayer(t); scheduleAnimation(); } @@ -1898,15 +1836,12 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { task.remove(false /* withTransition */, "removeTaskDisplayArea"); } else { // Reparent task to corresponding launch root or display area. - final WindowContainer launchRoot = - task.supportsSplitScreenWindowingModeInDisplayArea(toDisplayArea) - ? toDisplayArea.getLaunchRootTask( + final WindowContainer launchRoot = toDisplayArea.getLaunchRootTask( task.getWindowingMode(), task.getActivityType(), null /* options */, null /* sourceTask */, - 0 /* launchFlags */) - : null; + 0 /* launchFlags */); task.reparent(launchRoot == null ? toDisplayArea : launchRoot, POSITION_TOP); // Set the windowing mode to undefined by default to let the root task inherited the diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index eca0fd77fcb9..d77e9b397ef9 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -152,6 +152,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { final RootWindowContainer mRootWindowContainer; private final TaskFragmentOrganizerController mTaskFragmentOrganizerController; + // TODO(b/233177466): Move mMinWidth and mMinHeight to Task and remove usages in TaskFragment /** * Minimal width of this task fragment when it's resizeable. {@link #INVALID_MIN_SIZE} means it * should use the default minimal width. @@ -174,14 +175,6 @@ class TaskFragment extends WindowContainer<WindowContainer> { private TaskFragment mAdjacentTaskFragment; /** - * Whether to move adjacent task fragment together when re-positioning. - * - * @see #mAdjacentTaskFragment - */ - // TODO(b/207185041): Remove this once having a single-top root for split screen. - boolean mMoveAdjacentTogether; - - /** * Prevents duplicate calls to onTaskAppeared. */ boolean mTaskFragmentAppearedSent; @@ -333,15 +326,14 @@ class TaskFragment extends WindowContainer<WindowContainer> { return service.mWindowOrganizerController.getTaskFragment(token); } - void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment, boolean moveTogether) { + void setAdjacentTaskFragment(@Nullable TaskFragment taskFragment) { if (mAdjacentTaskFragment == taskFragment) { return; } resetAdjacentTaskFragment(); if (taskFragment != null) { mAdjacentTaskFragment = taskFragment; - mMoveAdjacentTogether = moveTogether; - taskFragment.setAdjacentTaskFragment(this, moveTogether); + taskFragment.setAdjacentTaskFragment(this); } } @@ -350,11 +342,9 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) { mAdjacentTaskFragment.mAdjacentTaskFragment = null; mAdjacentTaskFragment.mDelayLastActivityRemoval = false; - mAdjacentTaskFragment.mMoveAdjacentTogether = false; } mAdjacentTaskFragment = null; mDelayLastActivityRemoval = false; - mMoveAdjacentTogether = false; } void setTaskFragmentOrganizer(@NonNull TaskFragmentOrganizerToken organizer, int uid, @@ -535,6 +525,25 @@ class TaskFragment extends WindowContainer<WindowContainer> { || isAllowedToEmbedActivityInTrustedMode(a, uid); } + boolean smallerThanMinDimension(@NonNull ActivityRecord activity) { + final Rect taskFragBounds = getBounds(); + final Task task = getTask(); + // Don't need to check if the bounds match parent Task bounds because the fallback mechanism + // is to reparent the Activity to parent if minimum dimensions are not satisfied. + if (task == null || taskFragBounds.equals(task.getBounds())) { + return false; + } + final Point minDimensions = activity.getMinDimensions(); + if (minDimensions == null) { + return false; + } + final int minWidth = minDimensions.x; + final int minHeight = minDimensions.y; + final boolean smaller = taskFragBounds.width() < minWidth + || taskFragBounds.height() < minHeight; + return smaller; + } + /** * Checks if the organized task fragment is allowed to embed activity in untrusted mode. */ @@ -1534,7 +1543,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (prev.attachedToProcess()) { if (shouldAutoPip) { boolean didAutoPip = mAtmService.enterPictureInPictureMode( - prev, prev.pictureInPictureArgs); + prev, prev.pictureInPictureArgs, false /* fromClient */); ProtoLog.d(WM_DEBUG_STATES, "Auto-PIP allowed, entering PIP mode " + "directly: %s, didAutoPip: %b", prev, didAutoPip); } else { @@ -1572,8 +1581,11 @@ class TaskFragment extends WindowContainer<WindowContainer> { } else { prev.schedulePauseTimeout(); - // Unset readiness since we now need to wait until this pause is complete. - mTransitionController.setReady(this, false /* ready */); + // All activities will be stopped when sleeping, don't need to wait for pause. + if (!uiSleeping) { + // Unset readiness since we now need to wait until this pause is complete. + mTransitionController.setReady(this, false /* ready */); + } return true; } @@ -1755,7 +1767,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { mClearedTaskForReuse = false; mClearedTaskFragmentForPip = false; - boolean isAddingActivity = child.asActivityRecord() != null; + final ActivityRecord addingActivity = child.asActivityRecord(); + final boolean isAddingActivity = addingActivity != null; final Task task = isAddingActivity ? getTask() : null; // If this task had any activity before we added this one. @@ -1780,7 +1793,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { mBackScreenshots.put(r.mActivityComponent.flattenToString(), backBuffer); } child.asActivityRecord().inHistory = true; - task.onDescendantActivityAdded(taskHadActivity, activityType, child.asActivityRecord()); + task.onDescendantActivityAdded(taskHadActivity, activityType, addingActivity); } } @@ -1866,19 +1879,19 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (!mAtmService.mSupportsMultiWindow) { return false; } - final Task task = getTask(); - if (task == null) { + if (tda == null) { return false; } - if (tda == null) { + final Task task = getTask(); + if (task == null) { return false; } - if (!getTask().isResizeable() && !tda.supportsNonResizableMultiWindow()) { + if (!task.isResizeable() && !tda.supportsNonResizableMultiWindow()) { // Not support non-resizable in multi window. return false; } - final ActivityRecord rootActivity = getTask().getRootActivity(); + final ActivityRecord rootActivity = task.getRootActivity(); return tda.supportsActivityMinWidthHeightMultiWindow(mMinWidth, mMinHeight, rootActivity != null ? rootActivity.info : null); } @@ -2040,13 +2053,14 @@ class TaskFragment extends WindowContainer<WindowContainer> { } if (inOutConfig.screenWidthDp == Configuration.SCREEN_WIDTH_DP_UNDEFINED) { - final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density); + final int overrideScreenWidthDp = (int) (mTmpStableBounds.width() / density + 0.5f); inOutConfig.screenWidthDp = (insideParentBounds && !customContainerPolicy) ? Math.min(overrideScreenWidthDp, parentConfig.screenWidthDp) : overrideScreenWidthDp; } if (inOutConfig.screenHeightDp == Configuration.SCREEN_HEIGHT_DP_UNDEFINED) { - final int overrideScreenHeightDp = (int) (mTmpStableBounds.height() / density); + final int overrideScreenHeightDp = + (int) (mTmpStableBounds.height() / density + 0.5f); inOutConfig.screenHeightDp = (insideParentBounds && !customContainerPolicy) ? Math.min(overrideScreenHeightDp, parentConfig.screenHeightDp) : overrideScreenHeightDp; @@ -2065,8 +2079,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { if (WindowConfiguration.isFloating(windowingMode) && !inPipTransition) { // For floating tasks, calculate the smallest width from the bounds of the // task, because they should not be affected by insets. - inOutConfig.smallestScreenWidthDp = (int) ( - Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density); + inOutConfig.smallestScreenWidthDp = (int) (0.5f + + Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density); } else if (isEmbedded()) { // For embedded TFs, the smallest width should be updated. Otherwise, inherit // from the parent task would result in applications loaded wrong resource. @@ -2085,8 +2099,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { // For calculating screen layout, we need to use the non-decor inset screen area for the // calculation for compatibility reasons, i.e. screen area without system bars that // could never go away in Honeycomb. - int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density); - int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density); + int compatScreenWidthDp = (int) (mTmpNonDecorBounds.width() / density + 0.5f); + int compatScreenHeightDp = (int) (mTmpNonDecorBounds.height() / density + 0.5f); // Use overrides if provided. If both overrides are provided, mTmpNonDecorBounds is // undefined so it can't be used. if (inOutConfig.screenWidthDp != Configuration.SCREEN_WIDTH_DP_UNDEFINED) { @@ -2294,7 +2308,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { TaskFragmentInfo getTaskFragmentInfo() { List<IBinder> childActivities = new ArrayList<>(); for (int i = 0; i < getChildCount(); i++) { - final WindowContainer wc = getChildAt(i); + final WindowContainer<?> wc = getChildAt(i); final ActivityRecord ar = wc.asActivityRecord(); if (mTaskFragmentOrganizerUid != INVALID_UID && ar != null && ar.info.processName.equals(mTaskFragmentOrganizerProcessName) @@ -2314,7 +2328,31 @@ class TaskFragment extends WindowContainer<WindowContainer> { childActivities, positionInParent, mClearedTaskForReuse, - mClearedTaskFragmentForPip); + mClearedTaskFragmentForPip, + calculateMinDimension()); + } + + /** + * Calculates the minimum dimensions that this TaskFragment can be resized. + * @see TaskFragmentInfo#getMinimumWidth() + * @see TaskFragmentInfo#getMinimumHeight() + */ + Point calculateMinDimension() { + final int[] maxMinWidth = new int[1]; + final int[] maxMinHeight = new int[1]; + + forAllActivities(a -> { + if (a.finishing) { + return; + } + final Point minDimensions = a.getMinDimensions(); + if (minDimensions == null) { + return; + } + maxMinWidth[0] = Math.max(maxMinWidth[0], minDimensions.x); + maxMinHeight[0] = Math.max(maxMinHeight[0], minDimensions.y); + }); + return new Point(maxMinWidth[0], maxMinHeight[0]); } @Nullable diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java index 47c397d12720..03ca4fd49a96 100644 --- a/services/core/java/com/android/server/wm/TaskPositioner.java +++ b/services/core/java/com/android/server/wm/TaskPositioner.java @@ -41,7 +41,6 @@ import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.InputConfig; -import android.os.Process; import android.os.RemoteException; import android.os.Trace; import android.util.DisplayMetrics; @@ -222,8 +221,8 @@ class TaskPositioner implements IBinder.DeathRecipient { mDragWindowHandle.token = mClientChannel.getToken(); mDragWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_DRAG; mDragWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS; - mDragWindowHandle.ownerPid = Process.myPid(); - mDragWindowHandle.ownerUid = Process.myUid(); + mDragWindowHandle.ownerPid = WindowManagerService.MY_PID; + mDragWindowHandle.ownerUid = WindowManagerService.MY_UID; mDragWindowHandle.scaleFactor = 1.0f; // When dragging the window around, we do not want to steal focus for the window. mDragWindowHandle.inputConfig = InputConfig.NOT_FOCUSABLE; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index f6f9020555d8..84f6104652e3 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -27,6 +27,7 @@ import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS; import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_IS_RECENTS; @@ -45,6 +46,7 @@ import static android.view.WindowManager.TransitionType; import static android.view.WindowManager.transitTypeToString; import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; +import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD; import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; @@ -52,6 +54,8 @@ import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; +import static com.android.server.wm.ActivityRecord.State.RESUMED; +import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_SPLASH_SCREEN; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_WINDOWS_DRAWN; @@ -91,6 +95,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.function.Predicate; /** @@ -127,18 +132,27 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe */ private static final int STATE_ABORT = 3; + /** + * This transition has finished playing successfully. + */ + private static final int STATE_FINISHED = 4; + @IntDef(prefix = { "STATE_" }, value = { STATE_PENDING, STATE_COLLECTING, STATE_STARTED, STATE_PLAYING, - STATE_ABORT + STATE_ABORT, + STATE_FINISHED }) @Retention(RetentionPolicy.SOURCE) @interface TransitionState {} final @TransitionType int mType; private int mSyncId = -1; + // Used for tracking a Transition throughout a lifecycle (i.e. from STATE_COLLECTING to + // STATE_FINISHED or STATE_ABORT), and should only be used for testing and debugging. + private int mDebugId = -1; private @TransitionFlags int mFlags; private final TransitionController mController; private final BLASTSyncEngine mSyncEngine; @@ -198,6 +212,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mFlags = flags; mController = controller; mSyncEngine = syncEngine; + + controller.mTransitionTracer.logState(this); } void addFlag(int flag) { @@ -210,10 +226,26 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mTransientLaunches = new ArrayMap<>(); } mTransientLaunches.put(activity, restoreBelow); + setTransientLaunchToChanges(activity); + + if (restoreBelow != null) { + final ChangeInfo info = mChanges.get(restoreBelow); + info.mFlags |= ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH; + } ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Transition %d: Set %s as " + "transient-launch", mSyncId, activity); } + boolean isTransientHide(@NonNull Task task) { + if (mTransientLaunches == null) return false; + for (int i = 0; i < mTransientLaunches.size(); ++i) { + if (mTransientLaunches.valueAt(i) == task) { + return true; + } + } + return false; + } + boolean isTransientLaunch(@NonNull ActivityRecord activity) { return mTransientLaunches != null && mTransientLaunches.containsKey(activity); } @@ -238,11 +270,35 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe info.mFlags = info.mFlags | ChangeInfo.FLAG_SEAMLESS_ROTATION; } + /** + * Only set flag to the parent tasks and activity itself. + */ + private void setTransientLaunchToChanges(@NonNull WindowContainer wc) { + for (WindowContainer curr = wc; curr != null && mChanges.containsKey(curr); + curr = curr.getParent()) { + if (curr.asTask() == null && curr.asActivityRecord() == null) { + return; + } + final ChangeInfo info = mChanges.get(curr); + info.mFlags = info.mFlags | ChangeInfo.FLAG_TRANSIENT_LAUNCH; + } + } + + @TransitionState + int getState() { + return mState; + } + @VisibleForTesting int getSyncId() { return mSyncId; } + @VisibleForTesting + int getDebugId() { + return mDebugId; + } + @TransitionFlags int getFlags() { return mFlags; @@ -255,6 +311,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } mState = STATE_COLLECTING; mSyncId = mSyncEngine.startSyncSet(this, timeoutMs, TAG); + mDebugId = mSyncId; + + mController.mTransitionTracer.logState(this); } /** @@ -272,6 +331,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Starting Transition %d", mSyncId); applyReady(); + + mController.mTransitionTracer.logState(this); } /** @@ -296,7 +357,12 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } } if (mParticipants.contains(wc)) return; - mSyncEngine.addToSyncSet(mSyncId, wc); + // Wallpaper is like in a static drawn state unless display may have changes, so exclude + // the case to reduce transition latency waiting for the unchanged wallpaper to redraw. + final boolean needSyncDraw = !isWallpaper(wc) || mParticipants.contains(wc.mDisplayContent); + if (needSyncDraw) { + mSyncEngine.addToSyncSet(mSyncId, wc); + } ChangeInfo info = mChanges.get(wc); if (info == null) { info = new ChangeInfo(wc); @@ -419,7 +485,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe for (int i = mTargets.size() - 1; i >= 0; --i) { final WindowContainer target = mTargets.get(i); if (target.getParent() != null) { - final SurfaceControl targetLeash = getLeashSurface(target); + final SurfaceControl targetLeash = getLeashSurface(target, null /* t */); final SurfaceControl origParent = getOrigParentSurface(target); // Ensure surfaceControls are re-parented back into the hierarchy. t.reparent(targetLeash, origParent); @@ -428,14 +494,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe t.setPosition(targetLeash, tmpPos.x, tmpPos.y); final Rect clipRect; // No need to clip the display in case seeing the clipped content when during the - // display rotation. - if (target.asDisplayContent() != null) { + // display rotation. No need to clip activities because they rely on clipping on + // task layers. + if (target.asDisplayContent() != null || target.asActivityRecord() != null) { clipRect = null; - } else if (target.asActivityRecord() != null) { - // Always use parent bounds of activity because letterbox area (e.g. fixed - // aspect ratio or size compat mode) should be included. - clipRect = target.getParent().getRequestedOverrideBounds(); - clipRect.offset(-tmpPos.x, -tmpPos.y); } else { clipRect = target.getRequestedOverrideBounds(); clipRect.offset(-tmpPos.x, -tmpPos.y); @@ -474,6 +536,58 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mCanPipOnFinish = canPipOnFinish; } + private boolean didCommitTransientLaunch() { + if (mTransientLaunches == null) return false; + for (int j = 0; j < mTransientLaunches.size(); ++j) { + if (mTransientLaunches.keyAt(j).isVisibleRequested()) { + return true; + } + } + return false; + } + + /** + * Check if pip-entry is possible after finishing and enter-pip if it is. + * + * @return true if we are *guaranteed* to enter-pip. This means we return false if there's + * a chance we won't thus legacy-entry (via pause+userLeaving) will return false. + */ + private boolean checkEnterPipOnFinish(@NonNull ActivityRecord ar) { + if (!mCanPipOnFinish || !ar.isVisible() || ar.getTask() == null) return false; + + if (ar.pictureInPictureArgs != null && ar.pictureInPictureArgs.isAutoEnterEnabled()) { + if (didCommitTransientLaunch()) { + // force enable pip-on-task-switch now that we've committed to actually launching + // to the transient activity. + ar.supportsEnterPipOnTaskSwitch = true; + } + return mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs, + false /* fromClient */); + } + + // Legacy pip-entry (not via isAutoEnterEnabled). + boolean canPip = ar.getDeferHidingClient(); + if (!canPip && didCommitTransientLaunch()) { + // force enable pip-on-task-switch now that we've committed to actually launching to the + // transient activity, and then recalculate whether we can attempt pip. + ar.supportsEnterPipOnTaskSwitch = true; + canPip = ar.checkEnterPictureInPictureState( + "finishTransition", true /* beforeStopping */) + && ar.isState(RESUMED); + } + if (!canPip) return false; + try { + // Legacy PIP-enter requires pause event with user-leaving. + mController.mAtm.mTaskSupervisor.mUserLeaving = true; + ar.getTaskFragment().startPausing(false /* uiSleeping */, + null /* resuming */, "finishTransition"); + } finally { + mController.mAtm.mTaskSupervisor.mUserLeaving = false; + } + // Return false anyway because there's no guarantee that the app will enter pip. + return false; + } + /** * The transition has finished animating and is ready to finalize WM state. This should not * be called directly; use {@link TransitionController#finishTransition} instead. @@ -489,7 +603,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } // Commit all going-invisible containers - boolean activitiesWentInvisible = false; for (int i = 0; i < mParticipants.size(); ++i) { final ActivityRecord ar = mParticipants.valueAt(i).asActivityRecord(); if (ar != null) { @@ -501,32 +614,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // then doing commitVisibility here would actually be out-of-order and leave the // activity in a bad state. if (!visibleAtTransitionEnd && !ar.isVisibleRequested()) { - boolean commitVisibility = true; - if (mCanPipOnFinish && ar.isVisible() && ar.getTask() != null) { - if (ar.pictureInPictureArgs != null - && ar.pictureInPictureArgs.isAutoEnterEnabled()) { - if (mTransientLaunches != null) { - for (int j = 0; j < mTransientLaunches.size(); ++j) { - if (mTransientLaunches.keyAt(j).isVisibleRequested()) { - // force enable pip-on-task-switch now that we've committed - // to actually launching to the transient activity. - ar.supportsEnterPipOnTaskSwitch = true; - break; - } - } - } - mController.mAtm.enterPictureInPictureMode(ar, ar.pictureInPictureArgs); - // Avoid commit visibility to false here, or else we will get a sudden - // "flash" / surface going invisible for a split second. - commitVisibility = false; - } else if (ar.getDeferHidingClient()) { - // Legacy PIP-enter requires pause event with user-leaving. - mController.mAtm.mTaskSupervisor.mUserLeaving = true; - ar.getTaskFragment().startPausing(false /* uiSleeping */, - null /* resuming */, "finishTransition"); - mController.mAtm.mTaskSupervisor.mUserLeaving = false; - } - } + final boolean commitVisibility = !checkEnterPipOnFinish(ar); + // Avoid commit visibility if entering pip or else we will get a sudden + // "flash" / surface going invisible for a split second. if (commitVisibility) { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Commit activity becoming invisible: %s", ar); @@ -540,7 +630,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } ar.commitVisibility(false /* visible */, false /* performLayout */, true /* fromTransition */); - activitiesWentInvisible = true; } } if (mChanges.get(ar).mVisible != visibleAtTransitionEnd) { @@ -567,12 +656,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe mController.dispatchLegacyAppTransitionFinished(ar); } } - if (activitiesWentInvisible) { - // Always schedule stop processing when transition finishes because activities don't - // stop while they are in a transition thus their stop could still be pending. - mController.mAtm.mTaskSupervisor - .scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); - } + // Always schedule stop processing when transition finishes because activities don't + // stop while they are in a transition thus their stop could still be pending. + mController.mAtm.mTaskSupervisor + .scheduleProcessStoppingAndFinishingActivitiesIfNeeded(); sendRemoteCallback(mClientAnimationFinishCallback); @@ -614,6 +701,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe dc.removeImeSurfaceImmediately(); dc.handleCompleteDeferredRemoval(); } + + mState = STATE_FINISHED; + mController.mTransitionTracer.logState(this); } void abort() { @@ -675,7 +765,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe // Resolve the animating targets from the participants mTargets = calculateTargets(mParticipants, mChanges); - final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges); + final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, mChanges, + transaction); if (mOverrideOptions != null) { info.setAnimationOptions(mOverrideOptions); } @@ -685,8 +776,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe handleNonAppWindowsInTransition(dc, mType, mFlags); - reportStartReasonsToLogger(); - // The callback is only populated for custom activity-level client animations sendRemoteCallback(mClientAnimationStartCallback); @@ -728,6 +817,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe for (int i = mParticipants.size() - 1; i >= 0; --i) { final WindowContainer wc = mParticipants.valueAt(i); if (wc.asWindowToken() == null || !wc.isVisibleRequested()) continue; + // don't include transient launches, though, since those are only temporarily visible. + if (mTransientLaunches != null && wc.asActivityRecord() != null + && mTransientLaunches.containsKey(wc.asActivityRecord())) continue; mVisibleAtTransitionEndTokens.add(wc.asWindowToken()); } @@ -775,6 +867,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } mSyncId = -1; mOverrideOptions = null; + + reportStartReasonsToLogger(); } /** @@ -987,11 +1081,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe for (int i = mParticipants.size() - 1; i >= 0; --i) { ActivityRecord r = mParticipants.valueAt(i).asActivityRecord(); if (r == null || !r.mVisibleRequested) continue; + int transitionReason = APP_TRANSITION_WINDOWS_DRAWN; // At this point, r is "ready", but if it's not "ALL ready" then it is probably only // ready due to starting-window. - reasons.put(r, (r.mStartingData instanceof SplashScreenStartingData - && !r.mLastAllReadyAtSync) - ? APP_TRANSITION_SPLASH_SCREEN : APP_TRANSITION_WINDOWS_DRAWN); + if (r.mStartingData instanceof SplashScreenStartingData && !r.mLastAllReadyAtSync) { + transitionReason = APP_TRANSITION_SPLASH_SCREEN; + } else if (r.isActivityTypeHomeOrRecents() && isTransientLaunch(r)) { + transitionReason = APP_TRANSITION_RECENTS_ANIM; + } + reasons.put(r, transitionReason); } mController.mAtm.mTaskSupervisor.getActivityMetricsLogger().notifyTransitionStarting( reasons); @@ -1020,6 +1118,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe return wc.asWallpaperToken() != null; } + private static boolean isInputMethod(WindowContainer wc) { + return wc.getWindowType() == TYPE_INPUT_METHOD; + } + private static boolean occludesKeyguard(WindowContainer wc) { final ActivityRecord ar = wc.asActivityRecord(); if (ar != null) { @@ -1226,8 +1328,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } } - /** Gets the leash surface for a window container */ - private static SurfaceControl getLeashSurface(WindowContainer wc) { + /** + * Gets the leash surface for a window container. + * @param t a transaction to create leashes on when necessary (fixed rotation at token-level). + * If t is null, then this will not create any leashes, just use one if it is there -- + * this is relevant for building the finishTransaction since it needs to match the + * start state and not erroneously create a leash of its own. + */ + private static SurfaceControl getLeashSurface(WindowContainer wc, + @Nullable SurfaceControl.Transaction t) { final DisplayContent asDC = wc.asDisplayContent(); if (asDC != null) { // DisplayContent is the "root", so we use the windowing layer instead to avoid @@ -1239,7 +1348,8 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (asToken != null) { // WindowTokens can have a fixed-rotation applied to them. In the current // implementation this fact is hidden from the player, so we must create a leash. - final SurfaceControl leash = asToken.getOrCreateFixedRotationLeash(); + final SurfaceControl leash = t != null ? asToken.getOrCreateFixedRotationLeash(t) + : asToken.getFixedRotationLeash(); if (leash != null) return leash; } } @@ -1268,12 +1378,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe * Construct a TransitionInfo object from a set of targets and changes. Also populates the * root surface. * @param sortedTargets The targets sorted by z-order from top (index 0) to bottom. + * @param startT The start transaction - used to set-up new leashes. */ @VisibleForTesting @NonNull static TransitionInfo calculateTransitionInfo(@TransitionType int type, int flags, ArrayList<WindowContainer> sortedTargets, - ArrayMap<WindowContainer, ChangeInfo> changes) { + ArrayMap<WindowContainer, ChangeInfo> changes, + @Nullable SurfaceControl.Transaction startT) { final TransitionInfo out = new TransitionInfo(type, flags); WindowContainer<?> topApp = null; @@ -1311,10 +1423,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName( "Transition Root: " + leashReference.getName()).build(); - SurfaceControl.Transaction t = ancestor.mWmService.mTransactionFactory.get(); - t.setLayer(rootLeash, leashReference.getLastLayer()); - t.apply(); - t.close(); + startT.setLayer(rootLeash, leashReference.getLastLayer()); out.setRootLeash(rootLeash, ancestor.getBounds().left, ancestor.getBounds().top); // Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order. @@ -1324,7 +1433,7 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe final ChangeInfo info = changes.get(target); final TransitionInfo.Change change = new TransitionInfo.Change( target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken() - : null, getLeashSurface(target)); + : null, getLeashSurface(target, startT)); // TODO(shell-transitions): Use leash for non-organized windows. if (info.mParent != null) { change.setParent(info.mParent.mRemoteToken.toWindowContainerToken()); @@ -1497,10 +1606,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe * seamless rotation. This is currently only used by DisplayContent during fixed-rotation. */ private static final int FLAG_SEAMLESS_ROTATION = 1; + private static final int FLAG_TRANSIENT_LAUNCH = 2; + private static final int FLAG_ABOVE_TRANSIENT_LAUNCH = 4; @IntDef(prefix = { "FLAG_" }, value = { FLAG_NONE, - FLAG_SEAMLESS_ROTATION + FLAG_SEAMLESS_ROTATION, + FLAG_TRANSIENT_LAUNCH, + FLAG_ABOVE_TRANSIENT_LAUNCH }) @Retention(RetentionPolicy.SOURCE) @interface Flag {} @@ -1537,6 +1650,11 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } boolean hasChanged(@NonNull WindowContainer newState) { + // the task including transient launch must promote to root task + if ((mFlags & ChangeInfo.FLAG_TRANSIENT_LAUNCH) != 0 + || (mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) { + return true; + } // If it's invisible and hasn't changed visibility, always return false since even if // something changed, it wouldn't be a visible change. final boolean currVisible = newState.isVisibleRequested(); @@ -1552,6 +1670,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe @TransitionInfo.TransitionMode int getTransitMode(@NonNull WindowContainer wc) { + if ((mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) { + return TRANSIT_CLOSE; + } final boolean nowVisible = wc.isVisibleRequested(); if (nowVisible == mVisible) { return TRANSIT_CHANGE; @@ -1602,6 +1723,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (isWallpaper(wc)) { flags |= FLAG_IS_WALLPAPER; } + if (isInputMethod(wc)) { + flags |= FLAG_IS_INPUT_METHOD; + } if (occludesKeyguard(wc)) { flags |= FLAG_OCCLUDES_KEYGUARD; } @@ -1610,6 +1734,19 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } /** + * This transition will be considered not-ready until a corresponding call to + * {@link #continueTransitionReady} + */ + void deferTransitionReady() { + ++mReadyTracker.mDeferReadyDepth; + } + + /** This undoes one call to {@link #deferTransitionReady}. */ + void continueTransitionReady() { + --mReadyTracker.mDeferReadyDepth; + } + + /** * The transition sync mechanism has 2 parts: * 1. Whether all WM operations for a particular transition are "ready" (eg. did the app * launch or stop or get a new configuration?). @@ -1639,6 +1776,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe private boolean mReadyOverride = false; /** + * When non-zero, this transition is forced not-ready (even over setAllReady()). Use this + * (via deferTransitionReady/continueTransitionReady) for situations where we want to do + * bulk operations which could trigger surface-placement but the existing ready-state + * isn't known. + */ + private int mDeferReadyDepth = 0; + + /** * Adds a ready-group. Any setReady calls in this subtree will be tracked together. For * now these are only DisplayContents. */ @@ -1678,8 +1823,15 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe /** @return true if all tracked subtrees are ready. */ boolean allReady() { ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " allReady query: used=%b " - + "override=%b states=[%s]", mUsed, mReadyOverride, groupsToString()); + + "override=%b defer=%d states=[%s]", mUsed, mReadyOverride, mDeferReadyDepth, + groupsToString()); + // If the readiness has never been touched, mUsed will be false. We never want to + // consider a transition ready if nothing has been reported on it. if (!mUsed) return false; + // If we are deferring readiness, we never report ready. This is usually temporary. + if (mDeferReadyDepth > 0) return false; + // Next check all the ready groups to see if they are ready. We can short-cut this if + // ready-override is set (which is treated as "everything is marked ready"). if (mReadyOverride) return true; for (int i = mReadyGroups.size() - 1; i >= 0; --i) { final WindowContainer wc = mReadyGroups.keyAt(i); diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index c1c390eea932..6d31e937984e 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -75,6 +75,7 @@ class TransitionController { private ITransitionPlayer mTransitionPlayer; final TransitionMetricsReporter mTransitionMetricsReporter = new TransitionMetricsReporter(); + final TransitionTracer mTransitionTracer; private IApplicationThread mTransitionPlayerThread; final ActivityTaskManagerService mAtm; @@ -100,10 +101,12 @@ class TransitionController { final StatusBarManagerInternal mStatusBar; TransitionController(ActivityTaskManagerService atm, - TaskSnapshotController taskSnapshotController) { + TaskSnapshotController taskSnapshotController, + TransitionTracer transitionTracer) { mAtm = atm; mStatusBar = LocalServices.getService(StatusBarManagerInternal.class); mTaskSnapshotController = taskSnapshotController; + mTransitionTracer = transitionTracer; mTransitionPlayerDeath = () -> { synchronized (mAtm.mGlobalLock) { // Clean-up/finish any playing transitions. @@ -207,6 +210,18 @@ class TransitionController { } /** + * @return {@code true} if transition is actively collecting changes and `wc` is one of them + * or a descendant of one of them. {@code false} once playing. + */ + boolean inCollectingTransition(@NonNull WindowContainer wc) { + if (!isCollecting()) return false; + for (WindowContainer p = wc; p != null; p = p.getParent()) { + if (mCollectingTransition.mParticipants.contains(p)) return true; + } + return false; + } + + /** * @return {@code true} if transition is actively playing. This is not necessarily {@code true} * during collection. */ @@ -214,6 +229,18 @@ class TransitionController { return !mPlayingTransitions.isEmpty(); } + /** + * @return {@code true} if one of the playing transitions contains `wc`. + */ + boolean inPlayingTransition(@NonNull WindowContainer wc) { + for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { + for (WindowContainer p = wc; p != null; p = p.getParent()) { + if (mPlayingTransitions.get(i).mParticipants.contains(p)) return true; + } + } + return false; + } + /** @return {@code true} if a transition is running */ boolean inTransition() { // TODO(shell-transitions): eventually properly support multiple @@ -222,19 +249,7 @@ class TransitionController { /** @return {@code true} if a transition is running in a participant subtree of wc */ boolean inTransition(@NonNull WindowContainer wc) { - if (isCollecting()) { - for (WindowContainer p = wc; p != null; p = p.getParent()) { - if (isCollecting(p)) return true; - } - } - for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { - for (WindowContainer p = wc; p != null; p = p.getParent()) { - if (mPlayingTransitions.get(i).mParticipants.contains(p)) { - return true; - } - } - } - return false; + return inCollectingTransition(wc) || inPlayingTransition(wc); } boolean inRecentsTransition(@NonNull WindowContainer wc) { @@ -270,6 +285,16 @@ class TransitionController { return false; } + boolean isTransientHide(@NonNull Task task) { + if (mCollectingTransition != null && mCollectingTransition.isTransientHide(task)) { + return true; + } + for (int i = mPlayingTransitions.size() - 1; i >= 0; --i) { + if (mPlayingTransitions.get(i).isTransientHide(task)) return true; + } + return false; + } + /** * @return {@code true} if {@param ar} is part of a transient-launch activity in an active * transition. @@ -455,6 +480,24 @@ class TransitionController { setReady(wc, true); } + /** @see Transition#deferTransitionReady */ + void deferTransitionReady() { + if (!isShellTransitionsEnabled()) return; + if (mCollectingTransition == null) { + throw new IllegalStateException("No collecting transition to defer readiness for."); + } + mCollectingTransition.deferTransitionReady(); + } + + /** @see Transition#continueTransitionReady */ + void continueTransitionReady() { + if (!isShellTransitionsEnabled()) return; + if (mCollectingTransition == null) { + throw new IllegalStateException("No collecting transition to defer readiness for."); + } + mCollectingTransition.continueTransitionReady(); + } + /** @see Transition#finishTransition */ void finishTransition(@NonNull IBinder token) { // It is usually a no-op but make sure that the metric consumer is removed. @@ -484,6 +527,7 @@ class TransitionController { setAnimationRunning(true /* running */); } mPlayingTransitions.add(transition); + mTransitionTracer.logState(transition); } private void setAnimationRunning(boolean running) { @@ -502,6 +546,7 @@ class TransitionController { } transition.abort(); mCollectingTransition = null; + mTransitionTracer.logState(transition); } /** @@ -526,6 +571,12 @@ class TransitionController { } } + /** @see Transition#setCanPipOnFinish */ + void setCanPipOnFinish(boolean canPipOnFinish) { + if (mCollectingTransition == null) return; + mCollectingTransition.setCanPipOnFinish(canPipOnFinish); + } + void legacyDetachNavigationBarFromApp(@NonNull IBinder token) { final Transition transition = Transition.fromBinder(token); if (transition == null || !mPlayingTransitions.contains(transition)) { diff --git a/services/core/java/com/android/server/wm/TransitionTracer.java b/services/core/java/com/android/server/wm/TransitionTracer.java new file mode 100644 index 000000000000..192b9abc62a7 --- /dev/null +++ b/services/core/java/com/android/server/wm/TransitionTracer.java @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2022 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.server.wm; + +import static android.os.Build.IS_USER; + +import static com.android.server.wm.shell.ChangeInfo.CHANGE_FLAGS; +import static com.android.server.wm.shell.ChangeInfo.HAS_CHANGED; +import static com.android.server.wm.shell.ChangeInfo.TRANSIT_MODE; +import static com.android.server.wm.shell.ChangeInfo.WINDOW_IDENTIFIER; +import static com.android.server.wm.shell.Transition.CHANGE; +import static com.android.server.wm.shell.Transition.FLAGS; +import static com.android.server.wm.shell.Transition.ID; +import static com.android.server.wm.shell.Transition.STATE; +import static com.android.server.wm.shell.Transition.TIMESTAMP; +import static com.android.server.wm.shell.Transition.TRANSITION_TYPE; +import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER; +import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_H; +import static com.android.server.wm.shell.TransitionTraceProto.MAGIC_NUMBER_L; +import static com.android.server.wm.shell.TransitionTraceProto.TRANSITION; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.SystemClock; +import android.os.Trace; +import android.util.Log; +import android.util.proto.ProtoOutputStream; + +import com.android.internal.util.TraceBuffer; +import com.android.server.wm.Transition.ChangeInfo; +import com.android.server.wm.shell.TransitionTraceProto; + +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; + +/** + * Helper class to collect and dump transition traces. + */ +public class TransitionTracer { + + private static final String LOG_TAG = "TransitionTracer"; + + /** + * Maximum buffer size, currently defined as 5 MB + */ + private static final int BUFFER_CAPACITY = 5120 * 1024; // 5 MB + static final String WINSCOPE_EXT = ".winscope"; + private static final String TRACE_FILE = "/data/misc/wmtrace/transition_trace" + WINSCOPE_EXT; + private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; + + private final TransitionTraceBuffer mTraceBuffer = new TransitionTraceBuffer(); + + private final Object mEnabledLock = new Object(); + private volatile boolean mEnabled = false; + + private long mTraceStartTimestamp; + + private class TransitionTraceBuffer { + private final TraceBuffer mBuffer = new TraceBuffer(BUFFER_CAPACITY); + + private void pushTransitionState(Transition transition) { + final ProtoOutputStream outputStream = new ProtoOutputStream(); + final long transitionEntryToken = outputStream.start(TRANSITION); + + outputStream.write(ID, transition.getDebugId()); + outputStream.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos()); + outputStream.write(TRANSITION_TYPE, transition.mType); + outputStream.write(STATE, transition.getState()); + outputStream.write(FLAGS, transition.getFlags()); + + for (int i = 0; i < transition.mChanges.size(); ++i) { + final WindowContainer window = transition.mChanges.keyAt(i); + final ChangeInfo changeInfo = transition.mChanges.valueAt(i); + writeChange(outputStream, window, changeInfo); + } + + outputStream.end(transitionEntryToken); + + mBuffer.add(outputStream); + } + + private void writeChange(ProtoOutputStream outputStream, WindowContainer window, + ChangeInfo changeInfo) { + Trace.beginSection("TransitionProto#addChange"); + final long changeEntryToken = outputStream.start(CHANGE); + + final int transitMode = changeInfo.getTransitMode(window); + final boolean hasChanged = changeInfo.hasChanged(window); + final int changeFlags = changeInfo.getChangeFlags(window); + + outputStream.write(TRANSIT_MODE, transitMode); + outputStream.write(HAS_CHANGED, hasChanged); + outputStream.write(CHANGE_FLAGS, changeFlags); + window.writeIdentifierToProto(outputStream, WINDOW_IDENTIFIER); + + outputStream.end(changeEntryToken); + Trace.endSection(); + } + + public void writeToFile(File file, ProtoOutputStream proto) throws IOException { + mBuffer.writeTraceToFile(file, proto); + } + + public void reset() { + mBuffer.resetBuffer(); + } + } + + /** + * Records the current state of a transition in the transition trace (if it is running). + * @param transition the transition that we want to record the state of. + */ + public void logState(com.android.server.wm.Transition transition) { + if (!mEnabled) { + return; + } + + Log.d(LOG_TAG, "Logging state of transition " + transition); + mTraceBuffer.pushTransitionState(transition); + } + + /** + * Starts collecting transitions for the trace. + * If called while a trace is already running, this will reset the trace. + */ + public void startTrace(@Nullable PrintWriter pw) { + if (IS_USER) { + LogAndPrintln.e(pw, "Tracing is not supported on user builds."); + return; + } + Trace.beginSection("TransitionTracer#startTrace"); + LogAndPrintln.i(pw, "Starting shell transition trace."); + synchronized (mEnabledLock) { + mTraceStartTimestamp = SystemClock.elapsedRealtime(); + mEnabled = true; + mTraceBuffer.reset(); + } + Trace.endSection(); + } + + /** + * Stops collecting the transition trace and dump to trace to file. + * + * Dumps the trace to @link{TRACE_FILE}. + */ + public void stopTrace(@Nullable PrintWriter pw) { + stopTrace(pw, new File(TRACE_FILE)); + } + + /** + * Stops collecting the transition trace and dump to trace to file. + * @param outputFile The file to dump the transition trace to. + */ + public void stopTrace(@Nullable PrintWriter pw, File outputFile) { + if (IS_USER) { + LogAndPrintln.e(pw, "Tracing is not supported on user builds."); + return; + } + Trace.beginSection("TransitionTracer#stopTrace"); + LogAndPrintln.i(pw, "Stopping shell transition trace."); + synchronized (mEnabledLock) { + if (!mEnabled) { + LogAndPrintln.e(pw, + "Error: Tracing can't be stopped because it hasn't been started."); + return; + } + + mEnabled = false; + writeTraceToFileLocked(pw, outputFile); + } + Trace.endSection(); + } + + boolean isEnabled() { + return mEnabled; + } + + private void writeTraceToFileLocked(@Nullable PrintWriter pw, File file) { + Trace.beginSection("TransitionTracer#writeTraceToFileLocked"); + try { + ProtoOutputStream proto = new ProtoOutputStream(); + proto.write(MAGIC_NUMBER, MAGIC_NUMBER_VALUE); + proto.write(TransitionTraceProto.TIMESTAMP, mTraceStartTimestamp); + int pid = android.os.Process.myPid(); + LogAndPrintln.i(pw, "Writing file to " + file.getAbsolutePath() + + " from process " + pid); + mTraceBuffer.writeToFile(file, proto); + } catch (IOException e) { + LogAndPrintln.e(pw, "Unable to write buffer to file", e); + } + Trace.endSection(); + } + + private static class LogAndPrintln { + private static void i(@Nullable PrintWriter pw, String msg) { + Log.i(LOG_TAG, msg); + if (pw != null) { + pw.println(msg); + pw.flush(); + } + } + + private static void e(@Nullable PrintWriter pw, String msg) { + Log.e(LOG_TAG, msg); + if (pw != null) { + pw.println("ERROR: " + msg); + pw.flush(); + } + } + + private static void e(@Nullable PrintWriter pw, String msg, @NonNull Exception e) { + Log.e(LOG_TAG, msg, e); + if (pw != null) { + pw.println("ERROR: " + msg + " ::\n " + e); + pw.flush(); + } + } + } +} diff --git a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java index 5e963cc5ae8e..41c1e793dd90 100644 --- a/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java +++ b/services/core/java/com/android/server/wm/UnknownAppVisibilityController.java @@ -69,6 +69,10 @@ class UnknownAppVisibilityController { return mUnknownApps.isEmpty(); } + boolean isVisibilityUnknown(ActivityRecord r) { + return mUnknownApps.containsKey(r); + } + void clear() { mUnknownApps.clear(); } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index a32a6087a04d..6245005606d7 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -21,6 +21,7 @@ import static android.app.WallpaperManager.COMMAND_UNFREEZE; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; +import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; @@ -181,15 +182,11 @@ class WallpaperController { mFindResults.setUseTopWallpaperAsTarget(true); } - final RecentsAnimationController recentsAnimationController = - mService.getRecentsAnimationController(); final boolean animationWallpaper = animatingContainer != null && animatingContainer.getAnimation() != null && animatingContainer.getAnimation().getShowWallpaper(); final boolean hasWallpaper = w.hasWallpaper() || animationWallpaper; - final boolean isRecentsTransitionTarget = (recentsAnimationController != null - && recentsAnimationController.isWallpaperVisible(w)); - if (isRecentsTransitionTarget) { + if (isRecentsTransitionTarget(w)) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Found recents animation wallpaper target: " + w); mFindResults.setWallpaperTarget(w); return true; @@ -213,6 +210,22 @@ class WallpaperController { return false; }; + private boolean isRecentsTransitionTarget(WindowState w) { + if (w.mTransitionController.isShellTransitionsEnabled()) { + // Because the recents activity is invisible in background while keyguard is occluded + // (the activity window is on screen while keyguard is locked) with recents animation, + // the task animating by recents needs to be wallpaper target to make wallpaper visible. + // While for unlocked case, because recents activity will be moved to top, it can be + // the wallpaper target naturally. + return w.mActivityRecord != null && w.mAttrs.type == TYPE_BASE_APPLICATION + && mDisplayContent.isKeyguardLocked() + && w.mTransitionController.isTransientHide(w.getTask()); + } + // The window is either the recents activity or is in the task animating by the recents. + final RecentsAnimationController controller = mService.getRecentsAnimationController(); + return controller != null && controller.isWallpaperVisible(w); + } + /** * @see #computeLastWallpaperZoomOut() */ diff --git a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java index 9b6f4d947694..b000a9841d06 100644 --- a/services/core/java/com/android/server/wm/WindowContainerThumbnail.java +++ b/services/core/java/com/android/server/wm/WindowContainerThumbnail.java @@ -33,7 +33,6 @@ import android.graphics.GraphicBuffer; import android.graphics.PixelFormat; import android.graphics.Point; import android.hardware.HardwareBuffer; -import android.os.Process; import android.util.proto.ProtoOutputStream; import android.view.SurfaceControl; import android.view.SurfaceControl.Builder; @@ -91,7 +90,7 @@ class WindowContainerThumbnail implements Animatable { .setBLASTLayer() .setFormat(PixelFormat.TRANSLUCENT) .setMetadata(METADATA_WINDOW_TYPE, mWindowContainer.getWindowingMode()) - .setMetadata(METADATA_OWNER_UID, Process.myUid()) + .setMetadata(METADATA_OWNER_UID, WindowManagerService.MY_UID) .setCallsite("WindowContainerThumbnail") .build(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 902218621cd0..66fb4c9e5c94 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -238,10 +238,10 @@ import android.view.DisplayInfo; import android.view.Gravity; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.ICrossWindowBlurEnabledListener; +import android.view.IDisplayChangeWindowController; import android.view.IDisplayFoldListener; import android.view.IDisplayWindowInsetsController; import android.view.IDisplayWindowListener; -import android.view.IDisplayWindowRotationController; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; import android.view.IPinnedTaskListener; @@ -397,9 +397,8 @@ public class WindowManagerService extends IWindowManager.Stub private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular"; - // Used to indicate that if there is already a transition set, it should be preserved when - // trying to apply a new one. - private static final boolean ALWAYS_KEEP_CURRENT = true; + static final int MY_PID = myPid(); + static final int MY_UID = myUid(); static final int LOGTAG_INPUT_FOCUS = 62001; @@ -460,6 +459,7 @@ public class WindowManagerService extends IWindowManager.Stub final WindowManagerConstants mConstants; final WindowTracing mWindowTracing; + final TransitionTracer mTransitionTracer; private final DisplayAreaPolicy.Provider mDisplayAreaPolicyProvider; @@ -682,9 +682,9 @@ public class WindowManagerService extends IWindowManager.Stub final WallpaperVisibilityListeners mWallpaperVisibilityListeners = new WallpaperVisibilityListeners(); - IDisplayWindowRotationController mDisplayRotationController = null; - private final DeathRecipient mDisplayRotationControllerDeath = - () -> mDisplayRotationController = null; + IDisplayChangeWindowController mDisplayChangeController = null; + private final DeathRecipient mDisplayChangeControllerDeath = + () -> mDisplayChangeController = null; final DisplayWindowListenerController mDisplayNotificationController; final TaskSystemBarsListenerController mTaskSystemBarsListenerController; @@ -1067,7 +1067,6 @@ public class WindowManagerService extends IWindowManager.Stub Function<SurfaceSession, SurfaceControl.Builder> mSurfaceControlFactory; Supplier<SurfaceControl.Transaction> mTransactionFactory; - final Supplier<Surface> mSurfaceFactory; private final SurfaceControl.Transaction mTransaction; @@ -1150,7 +1149,7 @@ public class WindowManagerService extends IWindowManager.Stub final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm) { return main(context, im, showBootMsgs, onlyCore, policy, atm, - new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new, Surface::new, + new DisplayWindowSettingsProvider(), SurfaceControl.Transaction::new, SurfaceControl.Builder::new); } @@ -1163,12 +1162,11 @@ public class WindowManagerService extends IWindowManager.Stub final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm, DisplayWindowSettingsProvider displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory, - Supplier<Surface> surfaceFactory, Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { final WindowManagerService[] wms = new WindowManagerService[1]; DisplayThread.getHandler().runWithScissors(() -> wms[0] = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy, - atm, displayWindowSettingsProvider, transactionFactory, surfaceFactory, + atm, displayWindowSettingsProvider, transactionFactory, surfaceControlFactory), 0); return wms[0]; } @@ -1178,7 +1176,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void run() { WindowManagerPolicyThread.set(Thread.currentThread(), Looper.myLooper()); - mPolicy.init(mContext, WindowManagerService.this, WindowManagerService.this); + mPolicy.init(mContext, WindowManagerService.this); } }, 0); } @@ -1193,7 +1191,6 @@ public class WindowManagerService extends IWindowManager.Stub boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy, ActivityTaskManagerService atm, DisplayWindowSettingsProvider displayWindowSettingsProvider, Supplier<SurfaceControl.Transaction> transactionFactory, - Supplier<Surface> surfaceFactory, Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) { installLock(this, INDEX_WINDOW); mGlobalLock = atm.getGlobalLock(); @@ -1208,8 +1205,7 @@ public class WindowManagerService extends IWindowManager.Stub com.android.internal.R.bool.config_hasPermanentDpad); mInTouchMode = context.getResources().getBoolean( com.android.internal.R.bool.config_defaultInTouchMode); - inputManager.setInTouchMode( - mInTouchMode, myPid(), myUid(), /* hasPermission = */ true); + inputManager.setInTouchMode(mInTouchMode, MY_PID, MY_UID, true /* hasPermission */); mDrawLockTimeoutMillis = context.getResources().getInteger( com.android.internal.R.integer.config_drawLockTimeoutMillis); mAllowAnimationsInLowPowerMode = context.getResources().getBoolean( @@ -1233,7 +1229,6 @@ public class WindowManagerService extends IWindowManager.Stub mSurfaceControlFactory = surfaceControlFactory; mTransactionFactory = transactionFactory; - mSurfaceFactory = surfaceFactory; mTransaction = mTransactionFactory.get(); mPolicy = policy; @@ -1251,6 +1246,7 @@ public class WindowManagerService extends IWindowManager.Stub mWindowTracing = WindowTracing.createDefaultAndStartLooper(this, Choreographer.getInstance()); + mTransitionTracer = new TransitionTracer(); LocalServices.addService(WindowManagerPolicy.class, mPolicy); @@ -2704,7 +2700,7 @@ public class WindowManagerService extends IWindowManager.Stub } boolean checkCallingPermission(String permission, String func, boolean printLog) { - if (Binder.getCallingPid() == myPid()) { + if (Binder.getCallingPid() == MY_PID) { return true; } @@ -2864,7 +2860,7 @@ public class WindowManagerService extends IWindowManager.Stub // registration in DisplayContent#onParentChanged at DisplayContent initialization. final DisplayContent dc = mRoot.getDisplayContent(displayId); if (dc == null) { - if (Binder.getCallingPid() != myPid()) { + if (Binder.getCallingPid() != MY_PID) { throw new WindowManager.InvalidDisplayException("attachToDisplayContent: " + "trying to attach to a non-existing display:" + displayId); } @@ -4260,12 +4256,13 @@ public class WindowManagerService extends IWindowManager.Stub .notifyOnActivityRotation(displayContent.mDisplayId); } - final boolean pendingRemoteRotation = rotationChanged - && (displayContent.getDisplayRotation().isWaitingForRemoteRotation() + final boolean pendingRemoteDisplayChange = rotationChanged + && (displayContent.mRemoteDisplayChangeController + .isWaitingForRemoteDisplayChange() || displayContent.mTransitionController.isCollecting()); // Even if alwaysSend, we are waiting for a transition or remote to provide - // rotated configuration, so we can't update configuration yet. - if (!pendingRemoteRotation) { + // updated configuration, so we can't update configuration yet. + if (!pendingRemoteDisplayChange) { if (!rotationChanged || forceRelayout) { displayContent.setLayoutNeeded(); layoutNeeded = true; @@ -4297,17 +4294,17 @@ public class WindowManagerService extends IWindowManager.Stub } @Override - public void setDisplayWindowRotationController(IDisplayWindowRotationController controller) { + public void setDisplayChangeWindowController(IDisplayChangeWindowController controller) { mAtmService.enforceTaskPermission("setDisplayWindowRotationController"); try { synchronized (mGlobalLock) { - if (mDisplayRotationController != null) { - mDisplayRotationController.asBinder().unlinkToDeath( - mDisplayRotationControllerDeath, 0); - mDisplayRotationController = null; + if (mDisplayChangeController != null) { + mDisplayChangeController.asBinder().unlinkToDeath( + mDisplayChangeControllerDeath, 0); + mDisplayChangeController = null; } - controller.asBinder().linkToDeath(mDisplayRotationControllerDeath, 0); - mDisplayRotationController = controller; + controller.asBinder().linkToDeath(mDisplayChangeControllerDeath, 0); + mDisplayChangeController = controller; } } catch (RemoteException e) { throw new RuntimeException("Unable to set rotation controller"); @@ -5694,6 +5691,25 @@ public class WindowManagerService extends IWindowManager.Stub } } + void setSandboxDisplayApis(int displayId, boolean sandboxDisplayApis) { + if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Must hold permission " + WRITE_SECURE_SETTINGS); + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent displayContent = mRoot.getDisplayContent(displayId); + if (displayContent != null) { + displayContent.setSandboxDisplayApis(sandboxDisplayApis); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + /** The global settings only apply to default display. */ private boolean applyForcedPropertiesForDefaultDisplay() { boolean changed = false; @@ -5874,6 +5890,21 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void startTransitionTrace() { + mTransitionTracer.startTrace(null /* printwriter */); + } + + @Override + public void stopTransitionTrace() { + mTransitionTracer.stopTrace(null /* printwriter */); + } + + @Override + public boolean isTransitionTraceEnabled() { + return mTransitionTracer.isEnabled(); + } + + @Override public boolean registerCrossWindowBlurEnabledListener( ICrossWindowBlurEnabledListener listener) { return mBlurController.registerCrossWindowBlurEnabledListener(listener); @@ -6087,24 +6118,24 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayContent displayContent = mRoot.getDisplayContent(mFrozenDisplayId); final int numOpeningApps; final boolean waitingForConfig; - final boolean waitingForRemoteRotation; + final boolean waitingForRemoteDisplayChange; if (displayContent != null) { numOpeningApps = displayContent.mOpeningApps.size(); waitingForConfig = displayContent.mWaitingForConfig; - waitingForRemoteRotation = - displayContent.getDisplayRotation().isWaitingForRemoteRotation(); + waitingForRemoteDisplayChange = displayContent.mRemoteDisplayChangeController + .isWaitingForRemoteDisplayChange(); } else { - waitingForConfig = waitingForRemoteRotation = false; + waitingForConfig = waitingForRemoteDisplayChange = false; numOpeningApps = 0; } - if (waitingForConfig || waitingForRemoteRotation || mAppsFreezingScreen > 0 + if (waitingForConfig || waitingForRemoteDisplayChange || mAppsFreezingScreen > 0 || mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_ACTIVE || mClientFreezingScreen || numOpeningApps > 0) { ProtoLog.d(WM_DEBUG_ORIENTATION, "stopFreezingDisplayLocked: Returning " - + "waitingForConfig=%b, waitingForRemoteRotation=%b, " + + "waitingForConfig=%b, waitingForRemoteDisplayChange=%b, " + "mAppsFreezingScreen=%d, mWindowsFreezingScreen=%d, " + "mClientFreezingScreen=%b, mOpeningApps.size()=%d", - waitingForConfig, waitingForRemoteRotation, + waitingForConfig, waitingForRemoteDisplayChange, mAppsFreezingScreen, mWindowsFreezingScreen, mClientFreezingScreen, numOpeningApps); return; @@ -8753,7 +8784,7 @@ public class WindowManagerService extends IWindowManager.Stub @Override public boolean getWindowInsets(WindowManager.LayoutParams attrs, int displayId, InsetsState outInsetsState) { - final boolean fromLocal = Binder.getCallingPid() == myPid(); + final boolean fromLocal = Binder.getCallingPid() == MY_PID; final int uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); try { diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 34c93482ecfe..02f056cd33af 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -19,6 +19,19 @@ package com.android.server.wm; import static android.os.Build.IS_USER; import static android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_SOLID_COLOR; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_WALLPAPER; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER; +import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP; + +import android.content.res.Resources.NotFoundException; +import android.graphics.Color; import android.graphics.Point; import android.graphics.Rect; import android.os.ParcelFileDescriptor; @@ -36,6 +49,9 @@ import com.android.internal.os.ByteTransferPipe; import com.android.internal.protolog.ProtoLogImpl; import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.wm.LetterboxConfiguration.LetterboxBackgroundType; +import com.android.server.wm.LetterboxConfiguration.LetterboxHorizontalReachabilityPosition; +import com.android.server.wm.LetterboxConfiguration.LetterboxVerticalReachabilityPosition; import java.io.IOException; import java.io.PrintWriter; @@ -58,10 +74,12 @@ public class WindowManagerShellCommand extends ShellCommand { // Internal service impl -- must perform security checks before touching. private final WindowManagerService mInternal; + private final LetterboxConfiguration mLetterboxConfiguration; public WindowManagerShellCommand(WindowManagerService service) { mInterface = service; mInternal = service; + mLetterboxConfiguration = service.mLetterboxConfiguration; } @Override @@ -113,6 +131,14 @@ public class WindowManagerShellCommand extends ShellCommand { return runGetIgnoreOrientationRequest(pw); case "dump-visible-window-views": return runDumpVisibleWindowViews(pw); + case "set-letterbox-style": + return runSetLetterboxStyle(pw); + case "get-letterbox-style": + return runGetLetterboxStyle(pw); + case "reset-letterbox-style": + return runResetLetterboxStyle(pw); + case "set-sandbox-display-apis": + return runSandboxDisplayApis(pw); case "set-multi-window-config": return runSetMultiWindowConfig(); case "get-multi-window-config": @@ -123,6 +149,8 @@ public class WindowManagerShellCommand extends ShellCommand { return runReset(pw); case "disable-blur": return runSetBlurDisabled(pw); + case "shell": + return runWmShellCommand(pw); default: return handleDefaultCommands(cmd); } @@ -331,6 +359,37 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + /** + * Override display size and metrics to reflect the DisplayArea of the calling activity. + */ + private int runSandboxDisplayApis(PrintWriter pw) throws RemoteException { + int displayId = Display.DEFAULT_DISPLAY; + String arg = getNextArgRequired(); + if ("-d".equals(arg)) { + displayId = Integer.parseInt(getNextArgRequired()); + arg = getNextArgRequired(); + } + + final boolean sandboxDisplayApis; + switch (arg) { + case "true": + case "1": + sandboxDisplayApis = true; + break; + case "false": + case "0": + sandboxDisplayApis = false; + break; + default: + getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we " + + "get " + arg); + return -1; + } + + mInternal.setSandboxDisplayApis(displayId, sandboxDisplayApis); + return 0; + } + private int runDismissKeyguard(PrintWriter pw) throws RemoteException { mInterface.dismissKeyguard(null /* callback */, null /* message */); return 0; @@ -553,6 +612,497 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + private int runSetFixedOrientationLetterboxAspectRatio(PrintWriter pw) throws RemoteException { + final float aspectRatio; + try { + String arg = getNextArgRequired(); + aspectRatio = Float.parseFloat(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad aspect ratio format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: aspect ratio should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(aspectRatio); + } + return 0; + } + + private int runSetDefaultMinAspectRatioForUnresizableApps(PrintWriter pw) + throws RemoteException { + final float aspectRatio; + try { + String arg = getNextArgRequired(); + aspectRatio = Float.parseFloat(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad aspect ratio format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: aspect ratio should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setDefaultMinAspectRatioForUnresizableApps(aspectRatio); + } + return 0; + } + + private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException { + final int cornersRadius; + try { + String arg = getNextArgRequired(); + cornersRadius = Integer.parseInt(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad corners radius format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: corners radius should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxActivityCornersRadius(cornersRadius); + } + return 0; + } + + private int runSetLetterboxBackgroundType(PrintWriter pw) throws RemoteException { + @LetterboxBackgroundType final int backgroundType; + try { + String arg = getNextArgRequired(); + switch (arg) { + case "solid_color": + backgroundType = LETTERBOX_BACKGROUND_SOLID_COLOR; + break; + case "app_color_background": + backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND; + break; + case "app_color_background_floating": + backgroundType = LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING; + break; + case "wallpaper": + backgroundType = LETTERBOX_BACKGROUND_WALLPAPER; + break; + default: + getErrPrintWriter().println( + "Error: 'solid_color', 'app_color_background' or " + + "'wallpaper' should be provided as an argument"); + return -1; + } + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'solid_color', 'app_color_background' or " + + "'wallpaper' should be provided as an argument" + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType); + } + return 0; + } + + private int runSetLetterboxBackgroundColorResource(PrintWriter pw) throws RemoteException { + final int colorId; + try { + String arg = getNextArgRequired(); + colorId = mInternal.mContext.getResources() + .getIdentifier(arg, "color", "com.android.internal"); + } catch (NotFoundException e) { + getErrPrintWriter().println( + "Error: color in '@android:color/resource_name' format should be provided as " + + "an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxBackgroundColorResourceId(colorId); + } + return 0; + } + + private int runSetLetterboxBackgroundColor(PrintWriter pw) throws RemoteException { + final Color color; + try { + String arg = getNextArgRequired(); + color = Color.valueOf(Color.parseColor(arg)); + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: color in #RRGGBB format should be provided as " + + "an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxBackgroundColor(color); + } + return 0; + } + + private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw) + throws RemoteException { + final int radius; + try { + String arg = getNextArgRequired(); + radius = Integer.parseInt(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: blur radius format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: blur radius should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius); + } + return 0; + } + + private int runSetLetterboxBackgroundWallpaperDarkScrimAlpha(PrintWriter pw) + throws RemoteException { + final float alpha; + try { + String arg = getNextArgRequired(); + alpha = Float.parseFloat(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad alpha format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: alpha should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxBackgroundWallpaperDarkScrimAlpha(alpha); + } + return 0; + } + + private int runSetLetterboxHorizontalPositionMultiplier(PrintWriter pw) throws RemoteException { + final float multiplier; + try { + String arg = getNextArgRequired(); + multiplier = Float.parseFloat(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad multiplier format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: multiplier should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(multiplier); + } + return 0; + } + + private int runSetLetterboxVerticalPositionMultiplier(PrintWriter pw) throws RemoteException { + final float multiplier; + try { + String arg = getNextArgRequired(); + multiplier = Float.parseFloat(arg); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: bad multiplier format " + e); + return -1; + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: multiplier should be provided as an argument " + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(multiplier); + } + return 0; + } + + private int runSetLetterboxIsHorizontalReachabilityEnabled(PrintWriter pw) + throws RemoteException { + String arg = getNextArg(); + final boolean enabled; + switch (arg) { + case "true": + case "1": + enabled = true; + break; + case "false": + case "0": + enabled = false; + break; + default: + getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg); + return -1; + } + + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(enabled); + } + return 0; + } + + private int runSetLetterboxIsVerticalReachabilityEnabled(PrintWriter pw) + throws RemoteException { + String arg = getNextArg(); + final boolean enabled; + switch (arg) { + case "true": + case "1": + enabled = true; + break; + case "false": + case "0": + enabled = false; + break; + default: + getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg); + return -1; + } + + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setIsVerticalReachabilityEnabled(enabled); + } + return 0; + } + + private int runSetLetterboxDefaultPositionForHorizontalReachability(PrintWriter pw) + throws RemoteException { + @LetterboxHorizontalReachabilityPosition final int position; + try { + String arg = getNextArgRequired(); + switch (arg) { + case "left": + position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT; + break; + case "center": + position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER; + break; + case "right": + position = LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT; + break; + default: + getErrPrintWriter().println( + "Error: 'left', 'center' or 'right' are expected as an argument"); + return -1; + } + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'left', 'center' or 'right' are expected as an argument" + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setDefaultPositionForHorizontalReachability(position); + } + return 0; + } + + private int runSetLetterboxDefaultPositionForVerticalReachability(PrintWriter pw) + throws RemoteException { + @LetterboxVerticalReachabilityPosition final int position; + try { + String arg = getNextArgRequired(); + switch (arg) { + case "top": + position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP; + break; + case "center": + position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER; + break; + case "bottom": + position = LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM; + break; + default: + getErrPrintWriter().println( + "Error: 'top', 'center' or 'bottom' are expected as an argument"); + return -1; + } + } catch (IllegalArgumentException e) { + getErrPrintWriter().println( + "Error: 'top', 'center' or 'bottom' are expected as an argument" + e); + return -1; + } + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setDefaultPositionForVerticalReachability(position); + } + return 0; + } + + private int runSetLetterboxIsEducationEnabled(PrintWriter pw) throws RemoteException { + String arg = getNextArg(); + final boolean enabled; + switch (arg) { + case "true": + case "1": + enabled = true; + break; + case "false": + case "0": + enabled = false; + break; + default: + getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg); + return -1; + } + + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setIsEducationEnabled(enabled); + } + return 0; + } + + private int runSetLetterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled(PrintWriter pw) + throws RemoteException { + String arg = getNextArg(); + final boolean enabled; + switch (arg) { + case "true": + case "1": + enabled = true; + break; + case "false": + case "0": + enabled = false; + break; + default: + getErrPrintWriter().println("Error: expected true, 1, false, 0, but got " + arg); + return -1; + } + + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.setIsSplitScreenAspectRatioForUnresizableAppsEnabled(enabled); + } + return 0; + } + + private int runSetLetterboxStyle(PrintWriter pw) throws RemoteException { + if (peekNextArg() == null) { + getErrPrintWriter().println("Error: No arguments provided."); + } + while (peekNextArg() != null) { + String arg = getNextArg(); + switch (arg) { + case "--aspectRatio": + runSetFixedOrientationLetterboxAspectRatio(pw); + break; + case "--minAspectRatioForUnresizable": + runSetDefaultMinAspectRatioForUnresizableApps(pw); + break; + case "--cornerRadius": + runSetLetterboxActivityCornersRadius(pw); + break; + case "--backgroundType": + runSetLetterboxBackgroundType(pw); + break; + case "--backgroundColor": + runSetLetterboxBackgroundColor(pw); + break; + case "--backgroundColorResource": + runSetLetterboxBackgroundColorResource(pw); + break; + case "--wallpaperBlurRadius": + runSetLetterboxBackgroundWallpaperBlurRadius(pw); + break; + case "--wallpaperDarkScrimAlpha": + runSetLetterboxBackgroundWallpaperDarkScrimAlpha(pw); + break; + case "--horizontalPositionMultiplier": + runSetLetterboxHorizontalPositionMultiplier(pw); + break; + case "--verticalPositionMultiplier": + runSetLetterboxVerticalPositionMultiplier(pw); + break; + case "--isHorizontalReachabilityEnabled": + runSetLetterboxIsHorizontalReachabilityEnabled(pw); + break; + case "--isVerticalReachabilityEnabled": + runSetLetterboxIsVerticalReachabilityEnabled(pw); + break; + case "--defaultPositionForHorizontalReachability": + runSetLetterboxDefaultPositionForHorizontalReachability(pw); + break; + case "--defaultPositionForVerticalReachability": + runSetLetterboxDefaultPositionForVerticalReachability(pw); + break; + case "--isEducationEnabled": + runSetLetterboxIsEducationEnabled(pw); + break; + case "--isSplitScreenAspectRatioForUnresizableAppsEnabled": + runSetLetterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled(pw); + break; + default: + getErrPrintWriter().println( + "Error: Unrecognized letterbox style option: " + arg); + return -1; + } + } + return 0; + } + + private int runResetLetterboxStyle(PrintWriter pw) throws RemoteException { + if (peekNextArg() == null) { + resetLetterboxStyle(); + } + synchronized (mInternal.mGlobalLock) { + while (peekNextArg() != null) { + String arg = getNextArg(); + switch (arg) { + case "aspectRatio": + mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); + break; + case "minAspectRatioForUnresizable": + mLetterboxConfiguration.resetDefaultMinAspectRatioForUnresizableApps(); + break; + case "cornerRadius": + mLetterboxConfiguration.resetLetterboxActivityCornersRadius(); + break; + case "backgroundType": + mLetterboxConfiguration.resetLetterboxBackgroundType(); + break; + case "backgroundColor": + mLetterboxConfiguration.resetLetterboxBackgroundColor(); + break; + case "wallpaperBlurRadius": + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); + break; + case "wallpaperDarkScrimAlpha": + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); + break; + case "horizontalPositionMultiplier": + mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); + break; + case "verticalPositionMultiplier": + mLetterboxConfiguration.resetLetterboxVerticalPositionMultiplier(); + break; + case "isHorizontalReachabilityEnabled": + mLetterboxConfiguration.getIsHorizontalReachabilityEnabled(); + break; + case "isVerticalReachabilityEnabled": + mLetterboxConfiguration.getIsVerticalReachabilityEnabled(); + break; + case "defaultPositionForHorizontalReachability": + mLetterboxConfiguration.getDefaultPositionForHorizontalReachability(); + break; + case "defaultPositionForVerticalReachability": + mLetterboxConfiguration.getDefaultPositionForVerticalReachability(); + break; + case "isEducationEnabled": + mLetterboxConfiguration.getIsEducationEnabled(); + break; + case "isSplitScreenAspectRatioForUnresizableAppsEnabled": + mLetterboxConfiguration + .getIsSplitScreenAspectRatioForUnresizableAppsEnabled(); + break; + default: + getErrPrintWriter().println( + "Error: Unrecognized letterbox style option: " + arg); + return -1; + } + } + } + return 0; + } + private int runSetMultiWindowConfig() { if (peekNextArg() == null) { getErrPrintWriter().println("Error: No arguments provided."); @@ -627,6 +1177,107 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + private void resetLetterboxStyle() { + synchronized (mInternal.mGlobalLock) { + mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); + mLetterboxConfiguration.resetDefaultMinAspectRatioForUnresizableApps(); + mLetterboxConfiguration.resetLetterboxActivityCornersRadius(); + mLetterboxConfiguration.resetLetterboxBackgroundType(); + mLetterboxConfiguration.resetLetterboxBackgroundColor(); + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); + mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); + mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled(); + mLetterboxConfiguration.resetIsVerticalReachabilityEnabled(); + mLetterboxConfiguration.resetDefaultPositionForHorizontalReachability(); + mLetterboxConfiguration.resetDefaultPositionForVerticalReachability(); + mLetterboxConfiguration.resetIsEducationEnabled(); + mLetterboxConfiguration.resetIsSplitScreenAspectRatioForUnresizableAppsEnabled(); + } + } + + private int runGetLetterboxStyle(PrintWriter pw) throws RemoteException { + synchronized (mInternal.mGlobalLock) { + pw.println("Corner radius: " + + mLetterboxConfiguration.getLetterboxActivityCornersRadius()); + pw.println("Horizontal position multiplier: " + + mLetterboxConfiguration.getLetterboxHorizontalPositionMultiplier()); + pw.println("Vertical position multiplier: " + + mLetterboxConfiguration.getLetterboxVerticalPositionMultiplier()); + pw.println("Aspect ratio: " + + mLetterboxConfiguration.getFixedOrientationLetterboxAspectRatio()); + pw.println("Default min aspect ratio for unresizable apps: " + + mLetterboxConfiguration.getDefaultMinAspectRatioForUnresizableApps()); + pw.println("Is horizontal reachability enabled: " + + mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()); + pw.println("Is vertical reachability enabled: " + + mLetterboxConfiguration.getIsVerticalReachabilityEnabled()); + pw.println("Default position for horizontal reachability: " + + LetterboxConfiguration.letterboxHorizontalReachabilityPositionToString( + mLetterboxConfiguration.getDefaultPositionForHorizontalReachability())); + pw.println("Default position for vertical reachability: " + + LetterboxConfiguration.letterboxVerticalReachabilityPositionToString( + mLetterboxConfiguration.getDefaultPositionForVerticalReachability())); + pw.println("Is education enabled: " + + mLetterboxConfiguration.getIsEducationEnabled()); + pw.println("Is using split screen aspect ratio as aspect ratio for unresizable apps: " + + mLetterboxConfiguration + .getIsSplitScreenAspectRatioForUnresizableAppsEnabled()); + + pw.println("Background type: " + + LetterboxConfiguration.letterboxBackgroundTypeToString( + mLetterboxConfiguration.getLetterboxBackgroundType())); + pw.println(" Background color: " + Integer.toHexString( + mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb())); + pw.println(" Wallpaper blur radius: " + + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius()); + pw.println(" Wallpaper dark scrim alpha: " + + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha()); + } + return 0; + } + + private int runWmShellCommand(PrintWriter pw) { + String arg = getNextArg(); + + switch (arg) { + case "tracing": + return runWmShellTracing(pw); + case "help": + default: + return runHelp(pw); + } + } + + private int runHelp(PrintWriter pw) { + pw.println("Window Manager Shell commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" tracing <start/stop>"); + pw.println(" Start/stop shell transition tracing."); + + return 0; + } + + private int runWmShellTracing(PrintWriter pw) { + String arg = getNextArg(); + + switch (arg) { + case "start": + mInternal.mTransitionTracer.startTrace(pw); + break; + case "stop": + mInternal.mTransitionTracer.stopTrace(pw); + break; + default: + getErrPrintWriter() + .println("Error: expected 'start' or 'stop', but got '" + arg + "'"); + return -1; + } + + return 0; + } + private int runReset(PrintWriter pw) throws RemoteException { int displayId = getDisplayId(getNextArg()); @@ -651,6 +1302,12 @@ public class WindowManagerShellCommand extends ShellCommand { // set-ignore-orientation-request mInterface.setIgnoreOrientationRequest(displayId, false /* ignoreOrientationRequest */); + // set-letterbox-style + resetLetterboxStyle(); + + // set-sandbox-display-apis + mInternal.setSandboxDisplayApis(displayId, /* sandboxDisplayApis= */ true); + // set-multi-window-config runResetMultiWindowConfig(); @@ -685,7 +1342,12 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]"); pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] "); pw.println(" If app requested orientation should be ignored."); + pw.println(" set-sandbox-display-apis [true|1|false|0]"); + pw.println(" Sets override of Display APIs getRealSize / getRealMetrics to reflect "); + pw.println(" DisplayArea of the activity, or the window bounds if in letterbox or"); + pw.println(" Size Compat Mode."); + printLetterboxHelp(pw); printMultiWindowConfigHelp(pw); pw.println(" reset [-d DISPLAY_ID]"); @@ -698,6 +1360,83 @@ public class WindowManagerShellCommand extends ShellCommand { } } + private void printLetterboxHelp(PrintWriter pw) { + pw.println(" set-letterbox-style"); + pw.println(" Sets letterbox style using the following options:"); + pw.println(" --aspectRatio aspectRatio"); + pw.println(" Aspect ratio of letterbox for fixed orientation. If aspectRatio <= " + + LetterboxConfiguration.MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO); + pw.println(" both it and R.dimen.config_fixedOrientationLetterboxAspectRatio will"); + pw.println(" be ignored and framework implementation will determine aspect ratio."); + pw.println(" --minAspectRatioForUnresizable aspectRatio"); + pw.println(" Default min aspect ratio for unresizable apps which is used when an"); + pw.println(" app doesn't specify android:minAspectRatio. An exception will be"); + pw.println(" thrown if aspectRatio < " + + LetterboxConfiguration.MIN_UNRESIZABLE_ASPECT_RATIO); + pw.println(" --cornerRadius radius"); + pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,"); + pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be"); + pw.println(" ignored and corners of the activity won't be rounded."); + pw.println(" --backgroundType [reset|solid_color|app_color_background"); + pw.println(" |app_color_background_floating|wallpaper]"); + pw.println(" Type of background used in the letterbox mode."); + pw.println(" --backgroundColor color"); + pw.println(" Color of letterbox which is be used when letterbox background type"); + pw.println(" is 'solid-color'. Use (set)get-letterbox-style to check and control"); + pw.println(" letterbox background type. See Color#parseColor for allowed color"); + pw.println(" formats (#RRGGBB and some colors by name, e.g. magenta or olive)."); + pw.println(" --backgroundColorResource resource_name"); + pw.println(" Color resource name of letterbox background which is used when"); + pw.println(" background type is 'solid-color'. Use (set)get-letterbox-style to"); + pw.println(" check and control background type. Parameter is a color resource"); + pw.println(" name, for example, @android:color/system_accent2_50."); + pw.println(" --wallpaperBlurRadius radius"); + pw.println(" Blur radius for 'wallpaper' letterbox background. If radius <= 0"); + pw.println(" both it and R.dimen.config_letterboxBackgroundWallpaperBlurRadius"); + pw.println(" are ignored and 0 is used."); + pw.println(" --wallpaperDarkScrimAlpha alpha"); + pw.println(" Alpha of a black translucent scrim shown over 'wallpaper'"); + pw.println(" letterbox background. If alpha < 0 or >= 1 both it and"); + pw.println(" R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha are ignored"); + pw.println(" and 0.0 (transparent) is used instead."); + pw.println(" --horizontalPositionMultiplier multiplier"); + pw.println(" Horizontal position of app window center. If multiplier < 0 or > 1,"); + pw.println(" both it and R.dimen.config_letterboxHorizontalPositionMultiplier"); + pw.println(" are ignored and central position (0.5) is used."); + pw.println(" --verticalPositionMultiplier multiplier"); + pw.println(" Vertical position of app window center. If multiplier < 0 or > 1,"); + pw.println(" both it and R.dimen.config_letterboxVerticalPositionMultiplier"); + pw.println(" are ignored and central position (0.5) is used."); + pw.println(" --isHorizontalReachabilityEnabled [true|1|false|0]"); + pw.println(" Whether horizontal reachability repositioning is allowed for "); + pw.println(" letterboxed fullscreen apps in landscape device orientation."); + pw.println(" --isVerticalReachabilityEnabled [true|1|false|0]"); + pw.println(" Whether vertical reachability repositioning is allowed for "); + pw.println(" letterboxed fullscreen apps in portrait device orientation."); + pw.println(" --defaultPositionForHorizontalReachability [left|center|right]"); + pw.println(" Default position of app window when horizontal reachability is."); + pw.println(" enabled."); + pw.println(" --defaultPositionForVerticalReachability [top|center|bottom]"); + pw.println(" Default position of app window when vertical reachability is."); + pw.println(" enabled."); + pw.println(" --isEducationEnabled [true|1|false|0]"); + pw.println(" Whether education is allowed for letterboxed fullscreen apps."); + pw.println(" --isSplitScreenAspectRatioForUnresizableAppsEnabled [true|1|false|0]"); + pw.println(" Whether using split screen aspect ratio as a default aspect ratio for"); + pw.println(" unresizable apps."); + pw.println(" reset-letterbox-style [aspectRatio|cornerRadius|backgroundType"); + pw.println(" |backgroundColor|wallpaperBlurRadius|wallpaperDarkScrimAlpha"); + pw.println(" |horizontalPositionMultiplier|verticalPositionMultiplier"); + pw.println(" |isHorizontalReachabilityEnabled|isVerticalReachabilityEnabled"); + pw.println(" isEducationEnabled||defaultPositionMultiplierForHorizontalReachability"); + pw.println(" ||defaultPositionMultiplierForVerticalReachability]"); + pw.println(" Resets overrides to default values for specified properties separated"); + pw.println(" by space, e.g. 'reset-letterbox-style aspectRatio cornerRadius'."); + pw.println(" If no arguments provided, all values will be reset."); + pw.println(" get-letterbox-style"); + pw.println(" Prints letterbox style configuration."); + } + private void printMultiWindowConfigHelp(PrintWriter pw) { pw.println(" set-multi-window-config"); pw.println(" Sets options to determine if activity should be shown in multi window:"); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index ff3b4a5bb44f..d4d8dd8bceb1 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -18,6 +18,8 @@ package com.android.server.wm; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.app.ActivityManager.isStartResultSuccessful; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; @@ -42,6 +44,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; import static com.android.server.wm.ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_PINNED_TASK; import static com.android.server.wm.Task.FLAG_FORCE_HIDDEN_FOR_TASK_ORG; import static com.android.server.wm.WindowContainer.POSITION_BOTTOM; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -56,6 +59,7 @@ import android.content.ActivityNotFoundException; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.graphics.Point; import android.graphics.Rect; import android.os.Binder; import android.os.Bundle; @@ -115,7 +119,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_LAYOUT_DIRECTION; - static final int CONTROLLABLE_WINDOW_CONFIGS = WindowConfiguration.WINDOW_CONFIG_BOUNDS + static final int CONTROLLABLE_WINDOW_CONFIGS = WINDOW_CONFIG_BOUNDS | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS; private final ActivityTaskManagerService mService; @@ -145,7 +149,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } void setWindowManager(WindowManagerService wms) { - mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController); + mTransitionController = new TransitionController(mService, wms.mTaskSnapshotController, + wms.mTransitionTracer); mTransitionController.registerLegacyListener(wms.mActivityManagerAppTransitionNotifier); } @@ -395,6 +400,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub effects |= TRANSACT_EFFECTS_LIFECYCLE; } } + final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); + final int hopSize = hops.size(); ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>(); Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries = t.getChanges().entrySet().iterator(); @@ -413,17 +420,43 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } if (transition != null) transition.collect(wc); - if (finishTransition != null) { - // Deal with edge-cases in recents where it pretends to finish itself. - if ((entry.getValue().getChangeMask() - & WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) { + if ((entry.getValue().getChangeMask() + & WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) { + // Disable entering pip (eg. when recents pretends to finish itself) + if (finishTransition != null) { finishTransition.setCanPipOnFinish(false /* canPipOnFinish */); + } else if (transition != null) { + transition.setCanPipOnFinish(false /* canPipOnFinish */); + } + } + // A bit hacky, but we need to detect "remove PiP" so that we can "wrap" the + // setWindowingMode call in force-hidden. + boolean forceHiddenForPip = false; + if (wc.asTask() != null && wc.inPinnedWindowingMode() + && entry.getValue().getWindowingMode() == WINDOWING_MODE_UNDEFINED) { + // We are in pip and going to undefined. Now search hierarchy ops to determine + // whether we are removing pip or expanding pip. + for (int i = 0; i < hopSize; ++i) { + final WindowContainerTransaction.HierarchyOp hop = hops.get(i); + if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) continue; + final WindowContainer hopWc = WindowContainer.fromBinder( + hop.getContainer()); + if (!wc.equals(hopWc)) continue; + forceHiddenForPip = !hop.getToTop(); } } + if (forceHiddenForPip) { + wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */); + } - int containerEffect = applyWindowContainerChange(wc, entry.getValue()); + int containerEffect = applyWindowContainerChange(wc, entry.getValue(), + t.getErrorCallbackToken()); effects |= containerEffect; + if (forceHiddenForPip) { + wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */); + } + // Lifecycle changes will trigger ensureConfig for everything. if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { @@ -431,8 +464,6 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } } // Hierarchy changes - final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps(); - final int hopSize = hops.size(); if (hopSize > 0) { final boolean isInLockTaskMode = mService.isInLockTaskMode(); for (int i = 0; i < hopSize; ++i) { @@ -503,7 +534,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } } - private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) { + private int applyChanges(WindowContainer<?> container, + WindowContainerTransaction.Change change, @Nullable IBinder errorCallbackToken) { // The "client"-facing API should prevent bad changes; however, just in case, sanitize // masks here. final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS; @@ -511,6 +543,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub int effects = 0; final int windowingMode = change.getWindowingMode(); if (configMask != 0) { + + adjustBoundsForMinDimensionsIfNeeded(container, change, errorCallbackToken); + if (windowingMode > -1 && windowingMode != container.getWindowingMode()) { // Special handling for when we are setting a windowingMode in the same transaction. // Setting the windowingMode is going to call onConfigurationChanged so we don't @@ -544,6 +579,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + " windowing mode during locked task mode."); } + if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED) { + // Do not directly put the container into PINNED mode as it may not support it or + // the app may not want to enter it. Instead, send a signal to request PIP + // mode to the app if they wish to support it below in #applyTaskChanges. + return effects; + } + final int prevMode = container.getWindowingMode(); container.setWindowingMode(windowingMode); if (prevMode != container.getWindowingMode()) { @@ -556,6 +598,26 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return effects; } + private void adjustBoundsForMinDimensionsIfNeeded(WindowContainer<?> container, + WindowContainerTransaction.Change change, @Nullable IBinder errorCallbackToken) { + final TaskFragment taskFragment = container.asTaskFragment(); + if (taskFragment == null || !taskFragment.isEmbedded()) { + return; + } + if ((change.getWindowSetMask() & WINDOW_CONFIG_BOUNDS) == 0) { + return; + } + final WindowConfiguration winConfig = change.getConfiguration().windowConfiguration; + final Rect bounds = winConfig.getBounds(); + final Point minDimensions = taskFragment.calculateMinDimension(); + if (bounds.width() < minDimensions.x || bounds.height() < minDimensions.y) { + sendMinimumDimensionViolation(taskFragment, minDimensions, errorCallbackToken, + "setBounds:" + bounds); + // Sets the bounds to match parent bounds. + winConfig.setBounds(new Rect()); + } + } + private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) { int effects = 0; final SurfaceControl.Transaction t = c.getBoundsChangeTransaction(); @@ -580,6 +642,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds); } + if (c.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_PINNED + && !tr.inPinnedWindowingMode()) { + final ActivityRecord activity = tr.getTopNonFinishingActivity(); + if (activity != null) { + final boolean lastSupportsEnterPipOnTaskSwitch = + activity.supportsEnterPipOnTaskSwitch; + // Temporarily force enable enter PIP on task switch so that PIP is requested + // regardless of whether the activity is resumed or paused. + activity.supportsEnterPipOnTaskSwitch = true; + boolean canEnterPip = activity.checkEnterPictureInPictureState( + "applyTaskChanges", true /* beforeStopping */); + if (canEnterPip) { + canEnterPip = mService.mActivityClientController + .requestPictureInPictureMode(activity); + } + if (!canEnterPip) { + // Restore the flag to its previous state when the activity cannot enter PIP. + activity.supportsEnterPipOnTaskSwitch = lastSupportsEnterPipOnTaskSwitch; + } + } + } + return effects; } @@ -696,7 +780,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub final Bundle activityOptions = hop.getLaunchOptions(); final int result = mService.getActivityStartController() .startActivityInTaskFragment(tf, activityIntent, activityOptions, - hop.getCallingActivity(), caller.mUid, caller.mPid); + hop.getCallingActivity(), caller.mUid, caller.mPid, + errorCallbackToken); if (!isStartResultSuccessful(result)) { sendTaskFragmentOperationFailure(organizer, errorCallbackToken, convertStartFailureToThrowable(result, activityIntent)); @@ -740,6 +825,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } + if (parent.smallerThanMinDimension(activity)) { + sendMinimumDimensionViolation(parent, activity.getMinDimensions(), + errorCallbackToken, "reparentActivityToTask"); + break; + } + activity.reparent(parent, POSITION_TOP); effects |= TRANSACT_EFFECTS_LIFECYCLE; break; @@ -764,7 +855,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } - tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */); + tf1.setAdjacentTaskFragment(tf2); effects |= TRANSACT_EFFECTS_LIFECYCLE; final Bundle bundle = hop.getLaunchOptions(); @@ -806,22 +897,22 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub tf.getDisplayContent().setFocusedApp(targetFocus); break; } - default: { - // The other operations may change task order so they are skipped while in lock - // task mode. The above operations are still allowed because they don't move - // tasks. And it may be necessary such as clearing launch root after entering - // lock task mode. - if (isInLockTaskMode) { - Slog.w(TAG, "Skip applying hierarchy operation " + hop - + " while in lock task mode"); - return effects; - } - } - } - - switch (type) { case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: { - effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId); + effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId, + isInLockTaskMode); + break; + } + case HIERARCHY_OP_TYPE_LAUNCH_TASK: { + mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, + "launchTask HierarchyOp"); + final Bundle launchOpts = hop.getLaunchOptions(); + final int taskId = launchOpts.getInt( + WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); + launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); + final SafeActivityOptions safeOptions = + SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid); + waitAsyncStart(() -> mService.mTaskSupervisor.startActivityFromRecents( + caller.mPid, caller.mUid, taskId, safeOptions)); break; } case HIERARCHY_OP_TYPE_REORDER: @@ -831,6 +922,16 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub Slog.e(TAG, "Attempt to operate on detached container: " + wc); break; } + // There is no use case to ask the reparent operation in lock-task mode now, so keep + // skipping this operation as usual. + if (isInLockTaskMode && type == HIERARCHY_OP_TYPE_REPARENT) { + Slog.w(TAG, "Skip applying hierarchy operation " + hop + + " while in lock task mode"); + break; + } + if (isLockTaskModeViolation(wc.getParent(), wc.asTask(), isInLockTaskMode)) { + break; + } if (syncId >= 0) { addToSyncSet(syncId, wc); } @@ -856,19 +957,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub effects |= sanitizeAndApplyHierarchyOp(wc, hop); break; } - case HIERARCHY_OP_TYPE_LAUNCH_TASK: { - mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS, - "launchTask HierarchyOp"); - final Bundle launchOpts = hop.getLaunchOptions(); - final int taskId = launchOpts.getInt( - WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); - launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID); - final SafeActivityOptions safeOptions = - SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid); - waitAsyncStart(() -> mService.mTaskSupervisor.startActivityFromRecents( - caller.mPid, caller.mUid, taskId, safeOptions)); - break; + default: { + // The other operations may change task order so they are skipped while in lock + // task mode. The above operations are still allowed because they don't move + // tasks. And it may be necessary such as clearing launch root after entering + // lock task mode. + if (isInLockTaskMode) { + Slog.w(TAG, "Skip applying hierarchy operation " + hop + + " while in lock task mode"); + return effects; + } } + } + + switch (type) { case HIERARCHY_OP_TYPE_PENDING_INTENT: { String resolvedType = hop.getActivityIntent() != null ? hop.getActivityIntent().resolveTypeIfNeeded( @@ -953,6 +1055,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return effects; } + /** A helper method to send minimum dimension violation error to the client. */ + void sendMinimumDimensionViolation(TaskFragment taskFragment, Point minDimensions, + IBinder errorCallbackToken, String reason) { + if (taskFragment == null || taskFragment.getTaskFragmentOrganizer() == null) { + return; + } + final Throwable exception = new SecurityException("The task fragment's bounds:" + + taskFragment.getBounds() + " does not satisfy minimum dimensions:" + + minDimensions + " " + reason); + sendTaskFragmentOperationFailure(taskFragment.getTaskFragmentOrganizer(), + errorCallbackToken, exception); + } + /** * Post and wait for the result of the activity start to prevent potential deadlock against * {@link WindowManagerGlobalLock}. @@ -1047,8 +1162,25 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return TRANSACT_EFFECTS_LIFECYCLE; } + private boolean isLockTaskModeViolation(WindowContainer parent, Task task, + boolean isInLockTaskMode) { + if (!isInLockTaskMode || parent == null || task == null) { + return false; + } + final LockTaskController lockTaskController = mService.getLockTaskController(); + boolean taskViolation = lockTaskController.isLockTaskModeViolation(task); + if (!taskViolation && parent.asTask() != null) { + taskViolation = lockTaskController.isLockTaskModeViolation(parent.asTask()); + } + if (taskViolation) { + Slog.w(TAG, "Can't support the operation since in lock task mode violation. " + + " Task: " + task + " Parent : " + parent); + } + return taskViolation; + } + private int reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop, - @Nullable Transition transition, int syncId) { + @Nullable Transition transition, int syncId, boolean isInLockTaskMode) { WindowContainer<?> currentParent = hop.getContainer() != null ? WindowContainer.fromBinder(hop.getContainer()) : null; WindowContainer newParent = hop.getNewParent() != null @@ -1086,6 +1218,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub ? newParent.asTask().getDisplayArea() : newParent.asTaskDisplayArea(); final WindowContainer finalCurrentParent = currentParent; + final WindowContainer finalNewParent = newParent; Slog.i(TAG, "reparentChildrenTasksHierarchyOp" + " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop); @@ -1109,6 +1242,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub || !ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) { return false; } + if (isLockTaskModeViolation(finalNewParent, task, isInLockTaskMode)) { + return false; + } if (hop.getToTop()) { tasksToReparent.add(0, task); @@ -1153,7 +1289,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub Slog.e(TAG, "Attempt to set adjacent TaskFragment in PIP Task"); return 0; } - root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether()); + root1.setAdjacentTaskFragment(root2); return TRANSACT_EFFECTS_LIFECYCLE; } @@ -1164,14 +1300,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } private int applyWindowContainerChange(WindowContainer wc, - WindowContainerTransaction.Change c) { + WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) { sanitizeWindowContainer(wc); if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbeddedTaskFragmentInPip()) { // No override from organizer for embedded TaskFragment in a PIP Task. return 0; } - int effects = applyChanges(wc, c); + int effects = applyChanges(wc, c, errorCallbackToken); if (wc instanceof DisplayArea) { effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c); @@ -1515,8 +1651,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment); } - void reparentTaskFragment(@NonNull TaskFragment oldParent, @Nullable WindowContainer newParent, - @Nullable ITaskFragmentOrganizer organizer, @Nullable IBinder errorCallbackToken) { + void reparentTaskFragment(@NonNull TaskFragment oldParent, + @Nullable WindowContainer<?> newParent, @Nullable ITaskFragmentOrganizer organizer, + @Nullable IBinder errorCallbackToken) { final TaskFragment newParentTF; if (newParent == null) { // Use the old parent's parent if the caller doesn't specify the new parent. @@ -1554,6 +1691,14 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); return; } + final Point minDimensions = oldParent.calculateMinDimension(); + final Rect newParentBounds = newParentTF.getBounds(); + if (newParentBounds.width() < minDimensions.x + || newParentBounds.height() < minDimensions.y) { + sendMinimumDimensionViolation(newParentTF, minDimensions, errorCallbackToken, + "reparentTaskFragment"); + return; + } while (oldParent.hasChild()) { oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP); } diff --git a/services/core/java/com/android/server/wm/WindowOrientationListener.java b/services/core/java/com/android/server/wm/WindowOrientationListener.java index de87ab9dcce0..3e165e442d79 100644 --- a/services/core/java/com/android/server/wm/WindowOrientationListener.java +++ b/services/core/java/com/android/server/wm/WindowOrientationListener.java @@ -296,9 +296,9 @@ public abstract class WindowOrientationListener { /** * Whether the device is in the lock screen. - * @return returns true if the screen is locked. Otherwise, returns false. + * @return returns true if the key guard is showing on the lock screen. */ - public abstract boolean isKeyguardLocked(); + public abstract boolean isKeyguardShowingAndNotOccluded(); public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); @@ -1151,7 +1151,7 @@ public abstract class WindowOrientationListener { FrameworkStatsLog.DEVICE_ROTATED__ROTATION_EVENT_TYPE__ACTUAL_EVENT); if (isRotationResolverEnabled()) { - if (isKeyguardLocked()) { + if (isKeyguardShowingAndNotOccluded()) { if (mLastRotationResolution != ROTATION_UNSET && SystemClock.uptimeMillis() - mLastRotationResolutionTimeStamp < mRotationMemorizationTimeoutMillis) { diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index 40417a4857d3..3ff912cca091 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.ActivityManager.PROCESS_STATE_CACHED_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.content.res.Configuration.ASSETS_SEQ_UNDEFINED; @@ -24,7 +25,6 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.util.Preconditions.checkArgument; -import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.wm.ActivityRecord.State.DESTROYED; import static com.android.server.wm.ActivityRecord.State.DESTROYING; import static com.android.server.wm.ActivityRecord.State.PAUSED; @@ -39,6 +39,7 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT_MILLIS; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; +import static com.android.server.wm.WindowManagerService.MY_PID; import android.Manifest; import android.annotation.NonNull; @@ -195,6 +196,11 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio /** Whether the process configuration is waiting to be dispatched to the process. */ private boolean mHasPendingConfigurationChange; + /** If the process state is in (<=) the cached state, then defer delivery of the config. */ + private static final int CACHED_CONFIG_PROC_STATE = PROCESS_STATE_CACHED_ACTIVITY; + /** Whether {@link #mLastReportedConfiguration} is deferred by the cached state. */ + private volatile boolean mHasCachedConfiguration; + /** * Registered {@link DisplayArea} as a listener to override config changes. {@code null} if not * registered. @@ -316,8 +322,27 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio return mCurProcState; } + /** + * Sets the computed process state from the oom adjustment calculation. This is frequently + * called in activity manager's lock, so don't use window manager lock here. + */ + @HotPath(caller = HotPath.OOM_ADJUSTMENT) public void setReportedProcState(int repProcState) { + final int prevProcState = mRepProcState; mRepProcState = repProcState; + + // Deliver the cached config if the app changes from cached state to non-cached state. + final IApplicationThread thread = mThread; + if (prevProcState >= CACHED_CONFIG_PROC_STATE && repProcState < CACHED_CONFIG_PROC_STATE + && thread != null && mHasCachedConfiguration) { + final Configuration config; + synchronized (mLastReportedConfiguration) { + config = new Configuration(mLastReportedConfiguration); + } + // Schedule immediately to make sure the app component (e.g. receiver, service) can get + // the latest configuration in their lifecycle callbacks (e.g. onReceive, onCreate). + scheduleConfigurationChange(thread, config); + } } int getReportedProcState() { @@ -1126,6 +1151,13 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mAtm.mH.sendMessage(m); } + /** Refreshes oom adjustment and process state of this process. */ + void scheduleUpdateOomAdj() { + mAtm.mH.sendMessage(PooledLambda.obtainMessage(WindowProcessListener::updateProcessInfo, + mListener, false /* updateServiceConnectionActivities */, + false /* activityChange */, true /* updateOomAdj */)); + } + /** Makes the process have top state before oom-adj is computed from a posted message. */ void addToPendingTop() { mAtm.mAmInternal.addPendingTopUid(mUid, mPid, mThread); @@ -1328,12 +1360,22 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio @Override public void onConfigurationChanged(Configuration newGlobalConfig) { super.onConfigurationChanged(newGlobalConfig); - updateConfiguration(); - } + final Configuration config = getConfiguration(); + if (mLastReportedConfiguration.equals(config)) { + // Nothing changed. + if (Build.IS_DEBUGGABLE && mHasImeService) { + // TODO (b/135719017): Temporary log for debugging IME service. + Slog.w(TAG_CONFIGURATION, "Current config: " + config + + " unchanged for IME proc " + mName); + } + return; + } - @Override - public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { - super.onRequestedOverrideConfigurationChanged(overrideConfiguration); + if (mPauseConfigurationDispatchCount > 0) { + mHasPendingConfigurationChange = true; + return; + } + dispatchConfiguration(config); } @Override @@ -1359,25 +1401,6 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio resolvedConfig.seq = newParentConfig.seq; } - private void updateConfiguration() { - final Configuration config = getConfiguration(); - if (mLastReportedConfiguration.diff(config) == 0) { - // Nothing changed. - if (Build.IS_DEBUGGABLE && mHasImeService) { - // TODO (b/135719017): Temporary log for debugging IME service. - Slog.w(TAG_CONFIGURATION, "Current config: " + config - + " unchanged for IME proc " + mName); - } - return; - } - - if (mPauseConfigurationDispatchCount > 0) { - mHasPendingConfigurationChange = true; - return; - } - dispatchConfiguration(config); - } - void dispatchConfiguration(Configuration config) { mHasPendingConfigurationChange = false; if (mThread == null) { @@ -1388,29 +1411,47 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } return; } + + config.seq = mAtm.increaseConfigurationSeqLocked(); + setLastReportedConfiguration(config); + + // A cached process doesn't have running application components, so it is unnecessary to + // notify the configuration change. The last-reported-configuration is still set because + // setReportedProcState() should not write any fields that require WM lock. + if (mRepProcState >= CACHED_CONFIG_PROC_STATE) { + mHasCachedConfiguration = true; + // Because there are 2 volatile accesses in setReportedProcState(): mRepProcState and + // mHasCachedConfiguration, check again in case mRepProcState is changed but hasn't + // read the change of mHasCachedConfiguration. + if (mRepProcState >= CACHED_CONFIG_PROC_STATE) { + return; + } + } + + scheduleConfigurationChange(mThread, config); + } + + private void scheduleConfigurationChange(IApplicationThread thread, Configuration config) { ProtoLog.v(WM_DEBUG_CONFIGURATION, "Sending to proc %s new config %s", mName, config); if (Build.IS_DEBUGGABLE && mHasImeService) { // TODO (b/135719017): Temporary log for debugging IME service. Slog.v(TAG_CONFIGURATION, "Sending to IME proc " + mName + " new config " + config); } - + mHasCachedConfiguration = false; try { - config.seq = mAtm.increaseConfigurationSeqLocked(); - mAtm.getLifecycleManager().scheduleTransaction(mThread, + mAtm.getLifecycleManager().scheduleTransaction(thread, ConfigurationChangeItem.obtain(config)); - setLastReportedConfiguration(config); } catch (Exception e) { - Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change", e); + Slog.e(TAG_CONFIGURATION, "Failed to schedule configuration change: " + mOwner, e); } } void setLastReportedConfiguration(Configuration config) { - mLastReportedConfiguration.setTo(config); - } - - Configuration getLastReportedConfiguration() { - return mLastReportedConfiguration; + // Synchronize for the access from setReportedProcState(). + synchronized (mLastReportedConfiguration) { + mLastReportedConfiguration.setTo(config); + } } void pauseConfigurationDispatch() { @@ -1461,6 +1502,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio // config seq. This increment ensures that the client won't ignore the configuration. config.seq = mAtm.increaseConfigurationSeqLocked(); } + // LaunchActivityItem includes the latest process configuration. + mHasCachedConfiguration = false; return config; } @@ -1688,7 +1731,8 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio } pw.println(prefix + " Configuration=" + getConfiguration()); pw.println(prefix + " OverrideConfiguration=" + getRequestedOverrideConfiguration()); - pw.println(prefix + " mLastReportedConfiguration=" + mLastReportedConfiguration); + pw.println(prefix + " mLastReportedConfiguration=" + (mHasCachedConfiguration + ? ("(cached) " + mLastReportedConfiguration) : mLastReportedConfiguration)); final int stateFlags = mActivityStateFlags; if (stateFlags != ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 1abe24e926fe..c91bec9fb0c9 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -116,7 +116,6 @@ import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RESIZE; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_INSETS; -import static com.android.server.am.ActivityManagerService.MY_PID; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER; import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT; @@ -151,6 +150,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.H.WINDOW_STATE_BLAST_SYNC_TIMEOUT; import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION; +import static com.android.server.wm.WindowManagerService.MY_PID; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; @@ -616,11 +616,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP int mLastVisibleLayoutRotation = -1; /** - * Set when we need to report the orientation change to client to trigger a relayout. - */ - boolean mReportOrientationChanged; - - /** * How long we last kept the screen frozen. */ int mLastFreezeDuration; @@ -1541,13 +1536,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP || configChanged || insetsChanged || dragResizingChanged - || mReportOrientationChanged || shouldSendRedrawForSync()) { ProtoLog.v(WM_DEBUG_RESIZE, - "Resize reasons for w=%s: %s configChanged=%b " - + "dragResizingChanged=%b reportOrientationChanged=%b", + "Resize reasons for w=%s: %s configChanged=%b dragResizingChanged=%b", this, mWindowFrames.getInsetsChangedInfo(), - configChanged, dragResizingChanged, mReportOrientationChanged); + configChanged, dragResizingChanged); if (insetsChanged) { mWindowFrames.setInsetsChanged(false); @@ -2178,6 +2171,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return; } if (mActivityRecord != null) { + if (!mActivityRecord.mVisibleRequested) return; if (mActivityRecord.allDrawn) { // The allDrawn of activity is reset when the visibility is changed to visible, so // the content should be ready if allDrawn is set. @@ -3580,10 +3574,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mAnimatingExit = false; ProtoLog.d(WM_DEBUG_ANIM, "Clear animatingExit: reason=destroySurface win=%s", this); - // Clear the flag so the buffer requested for the next new surface won't be dropped by - // mistaking the surface size needs to update. - mReportOrientationChanged = false; - if (useBLASTSync()) { immediatelyNotifyBlastSync(); } @@ -3900,12 +3890,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP ProtoLog.i(WM_DEBUG_ORIENTATION, "Resizing %s WITH DRAW PENDING", this); } - final boolean reportOrientation = mReportOrientationChanged; // Always reset these states first, so if {@link IWindow#resized} fails, this // window won't be added to {@link WindowManagerService#mResizingWindows} and set // {@link #mOrientationChanging} to true again by {@link #updateResizingWindowIfNeeded} // that may cause WINDOW_FREEZE_TIMEOUT because resizing the client keeps failing. - mReportOrientationChanged = false; mDragResizingChangeReported = true; mWindowFrames.clearReportResizeHints(); @@ -3914,7 +3902,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final boolean syncRedraw = shouldSendRedrawForSync(); final boolean reportDraw = syncRedraw || drawPending; final boolean isDragResizeChanged = isDragResizeChanged(); - final boolean forceRelayout = syncRedraw || reportOrientation || isDragResizeChanged; + final boolean forceRelayout = syncRedraw || isDragResizeChanged; final DisplayContent displayContent = getDisplayContent(); final boolean alwaysConsumeSystemBars = displayContent.getDisplayPolicy().areSystemBarsForcedShownLw(); @@ -3941,7 +3929,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration, getCompatInsetsState(), forceRelayout, alwaysConsumeSystemBars, displayId, mSyncSeqId, resizeMode); - if (drawPending && reportOrientation && mOrientationChanging) { + if (drawPending && mOrientationChanging) { mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime(); ProtoLog.v(WM_DEBUG_ORIENTATION, "Requested redraw for orientation change: %s", this); @@ -4366,12 +4354,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " mDestroying=" + mDestroying + " mRemoved=" + mRemoved); } - if (getOrientationChanging() || mAppFreezing || mReportOrientationChanged) { + if (getOrientationChanging() || mAppFreezing) { pw.println(prefix + "mOrientationChanging=" + mOrientationChanging + " configOrientationChanging=" + (getLastReportedConfiguration().orientation != getConfiguration().orientation) - + " mAppFreezing=" + mAppFreezing - + " mReportOrientationChanged=" + mReportOrientationChanged); + + " mAppFreezing=" + mAppFreezing); } if (mLastFreezeDuration != 0) { pw.print(prefix + "mLastFreezeDuration="); @@ -5572,7 +5559,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mLastSurfaceInsets.set(mAttrs.surfaceInsets); } if (surfaceSizeChanged && mWinAnimator.getShown() && !canPlayMoveAnimation() - && okToDisplay()) { + && okToDisplay() && mSyncState == SYNC_STATE_NONE) { applyWithNextDraw(mSetSurfacePositionConsumer); } else { mSetSurfacePositionConsumer.accept(t); @@ -6009,7 +5996,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // We could be more subtle with Integer.MAX_VALUE and track a seqId in the timeout. finishDrawing(null, Integer.MAX_VALUE); mWmService.mH.removeMessages(WINDOW_STATE_BLAST_SYNC_TIMEOUT, this); - if (!useBLASTSync()) return; } @Override @@ -6046,6 +6032,11 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mRedrawForSyncReported) { return false; } + if (mInRelayout) { + // The last sync seq id will return to the client, so there is no need to request the + // client to redraw. + return false; + } return useBLASTSync(); } diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index 5f43800bd9d5..fd379bf1d9f4 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -139,6 +139,12 @@ class WindowSurfaceController { "Destroying surface %s called by %s", this, Debug.getCallers(8)); try { if (mSurfaceControl != null) { + if (mAnimator.mIsWallpaper && !mAnimator.mWin.mWindowRemovalAllowed + && !mAnimator.mWin.mRemoveOnExit) { + // The wallpaper surface should have the same lifetime as its window. + Slog.e(TAG, "Unexpected removing wallpaper surface of " + mAnimator.mWin + + " by " + Debug.getCallers(8)); + } t.remove(mSurfaceControl); } } catch (RuntimeException e) { diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index d2e56faa0914..ddbb930ee20c 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -18,7 +18,6 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; @@ -37,6 +36,7 @@ import static com.android.server.wm.WindowTokenProto.WAITING_TO_SHOW; import static com.android.server.wm.WindowTokenProto.WINDOW_CONTAINER; import android.annotation.CallSuper; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.Rect; @@ -363,11 +363,7 @@ class WindowToken extends WindowContainer<WindowState> { @Override void assignLayer(SurfaceControl.Transaction t, int layer) { - if (windowType == TYPE_DOCK_DIVIDER) { - // See {@link DisplayContent#mSplitScreenDividerAnchor} - super.assignRelativeLayer(t, - mDisplayContent.getDefaultTaskDisplayArea().getSplitScreenDividerAnchor(), 1); - } else if (mRoundedCornerOverlay) { + if (mRoundedCornerOverlay) { super.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1); } else { super.assignLayer(t, layer); @@ -569,13 +565,12 @@ class WindowToken extends WindowContainer<WindowState> { * the same rotation. */ @Nullable - SurfaceControl getOrCreateFixedRotationLeash() { + SurfaceControl getOrCreateFixedRotationLeash(@NonNull SurfaceControl.Transaction t) { if (!mTransitionController.isShellTransitionsEnabled()) return null; final int rotation = getRelativeDisplayRotation(); if (rotation == Surface.ROTATION_0) return mFixedRotationTransformLeash; if (mFixedRotationTransformLeash != null) return mFixedRotationTransformLeash; - final SurfaceControl.Transaction t = getSyncTransaction(); final SurfaceControl leash = makeSurface().setContainerLayer() .setParent(getParentSurfaceControl()) .setName(getSurfaceControl() + " - rotation-leash") @@ -591,6 +586,15 @@ class WindowToken extends WindowContainer<WindowState> { return mFixedRotationTransformLeash; } + /** + * @return the leash which represents this window as if it was non-rotated. Will be null if + * there isn't one. + */ + @Nullable + SurfaceControl getFixedRotationLeash() { + return mFixedRotationTransformLeash; + } + void removeFixedRotationLeash() { if (mFixedRotationTransformLeash == null) return; final SurfaceControl.Transaction t = getSyncTransaction(); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 870257802608..48ad62231c11 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -12517,8 +12517,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .setContentIntent(locationSettingsIntent) .setAutoCancel(true) .build(); - mInjector.getNotificationManager().notify(SystemMessage.NOTE_LOCATION_CHANGED, - notification); + mHandler.post(() -> mInjector.getNotificationManager().notify( + SystemMessage.NOTE_LOCATION_CHANGED, notification)); } private String getLocationChangedTitle() { diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java index c64ff9e128e6..3de65c19a1a4 100644 --- a/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/WindowOrientationListenerTest.java @@ -185,7 +185,7 @@ public class WindowOrientationListenerTest { } @Override - public boolean isKeyguardLocked() { + public boolean isKeyguardShowingAndNotOccluded() { return mIsScreenLocked; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 2f054b004d42..8ba9af0d85d0 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -840,6 +840,156 @@ public class ManagedServicesTest extends UiServiceTestCase { } @Test + public void reregisterService_checksAppIsApproved_pkg() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_PACKAGE); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.addApprovedList("a", 0, true); + + service.reregisterService(cn, 0); + + assertTrue(service.isBound(cn, 0)); + } + + @Test + public void reregisterService_checksAppIsApproved_pkg_secondary() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_PACKAGE); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.addApprovedList("a", 0, false); + + service.reregisterService(cn, 0); + + assertTrue(service.isBound(cn, 0)); + } + + @Test + public void reregisterService_checksAppIsApproved_cn() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_COMPONENT); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.addApprovedList("a/a", 0, true); + + service.reregisterService(cn, 0); + + assertTrue(service.isBound(cn, 0)); + } + + @Test + public void reregisterService_checksAppIsApproved_cn_secondary() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_COMPONENT); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.addApprovedList("a/a", 0, false); + + service.reregisterService(cn, 0); + + assertTrue(service.isBound(cn, 0)); + } + + @Test + public void reregisterService_checksAppIsNotApproved_cn_secondary() throws Exception { + Context context = mock(Context.class); + PackageManager pm = mock(PackageManager.class); + ApplicationInfo ai = new ApplicationInfo(); + ai.targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; + + when(context.getPackageName()).thenReturn(mContext.getPackageName()); + when(context.getUserId()).thenReturn(mContext.getUserId()); + when(context.getPackageManager()).thenReturn(pm); + when(pm.getApplicationInfo(anyString(), anyInt())).thenReturn(ai); + + ManagedServices service = new TestManagedServices(context, mLock, mUserProfiles, mIpm, + APPROVAL_BY_COMPONENT); + ComponentName cn = ComponentName.unflattenFromString("a/a"); + + when(context.bindServiceAsUser(any(), any(), anyInt(), any())).thenAnswer(invocation -> { + Object[] args = invocation.getArguments(); + ServiceConnection sc = (ServiceConnection) args[1]; + sc.onServiceConnected(cn, mock(IBinder.class)); + return true; + }); + + service.addApprovedList("b/b", 0, false); + + service.reregisterService(cn, 0); + + assertFalse(service.isBound(cn, 0)); + } + + @Test public void unbindOtherUserServices() throws PackageManager.NameNotFoundException { Context context = mock(Context.class); PackageManager pm = mock(PackageManager.class); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java index 76d4059eb436..c5131c8f8c1d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java @@ -28,6 +28,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.atLeast; @@ -40,6 +41,9 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.INotificationManager; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.content.ComponentName; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; @@ -49,6 +53,7 @@ import android.os.Bundle; import android.os.UserHandle; import android.service.notification.NotificationListenerFilter; import android.service.notification.NotificationListenerService; +import android.service.notification.NotificationRankingUpdate; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.testing.TestableContext; @@ -60,6 +65,8 @@ import android.util.Xml; import com.android.server.UiServiceTestCase; +import com.google.common.collect.ImmutableList; + import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -397,6 +404,36 @@ public class NotificationListenersTest extends UiServiceTestCase { } @Test + public void testImplicitGrant() { + String pkg = "pkg"; + int uid = 9; + NotificationChannel channel = new NotificationChannel("id", "name", + NotificationManager.IMPORTANCE_HIGH); + Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setTimeoutAfter(1); + + StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, 8, "tag", uid, 0, + nb.build(), UserHandle.getUserHandleForUid(uid), null, 0); + NotificationRecord r = new NotificationRecord(mContext, sbn, channel); + + ManagedServices.ManagedServiceInfo info = mListeners.new ManagedServiceInfo( + null, new ComponentName("a", "a"), sbn.getUserId(), false, null, 33, 33); + List<ManagedServices.ManagedServiceInfo> services = ImmutableList.of(info); + when(mListeners.getServices()).thenReturn(services); + + when(mNm.isVisibleToListener(any(), anyInt(), any())).thenReturn(true); + when(mNm.makeRankingUpdateLocked(info)).thenReturn(mock(NotificationRankingUpdate.class)); + mNm.mPackageManagerInternal = mPmi; + + mListeners.notifyPostedLocked(r, null); + + verify(mPmi).grantImplicitAccess(sbn.getUserId(), null, UserHandle.getAppId(33), + sbn.getUid(), false, false); + } + + @Test public void testNotifyPostedLockedInLockdownMode() { NotificationRecord r = mock(NotificationRecord.class); NotificationRecord old = mock(NotificationRecord.class); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 8c4a4c9ea284..19f25c6bef9a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1786,13 +1786,16 @@ public class ActivityRecordTests extends WindowTestsBase { public void testActivityOnCancelFixedRotationTransform() { final ActivityRecord activity = createActivityWithTask(); final DisplayRotation displayRotation = activity.mDisplayContent.getDisplayRotation(); + final RemoteDisplayChangeController remoteDisplayChangeController = activity + .mDisplayContent.mRemoteDisplayChangeController; spyOn(displayRotation); + spyOn(remoteDisplayChangeController); final DisplayContent display = activity.mDisplayContent; final int originalRotation = display.getRotation(); // Make {@link DisplayContent#sendNewConfiguration} not apply rotation immediately. - doReturn(true).when(displayRotation).isWaitingForRemoteRotation(); + doReturn(true).when(remoteDisplayChangeController).isWaitingForRemoteDisplayChange(); doReturn((originalRotation + 1) % 4).when(displayRotation).rotationForOrientation( anyInt() /* orientation */, anyInt() /* lastRotation */); // Set to visible so the activity can freeze the screen. @@ -1830,7 +1833,7 @@ public class ActivityRecordTests extends WindowTestsBase { // Simulate the remote rotation has completed and the configuration doesn't change, then // the rotated activity should also be restored by clearing the transform. displayRotation.updateRotationUnchecked(true /* forceUpdate */); - doReturn(false).when(displayRotation).isWaitingForRemoteRotation(); + doReturn(false).when(remoteDisplayChangeController).isWaitingForRemoteDisplayChange(); clearInvocations(activity); display.setFixedRotationLaunchingAppUnchecked(activity); display.sendNewConfiguration(); @@ -2165,39 +2168,6 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test - public void testSupportsSplitScreenWindowingMode() { - final ActivityRecord activity = new ActivityBuilder(mAtm) - .setCreateTask(true) - .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE) - .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE) - .build(); - - // Not allow non-resizable - mAtm.mForceResizableActivities = false; - mAtm.mSupportsNonResizableMultiWindow = -1; - mAtm.mDevEnableNonResizableMultiWindow = false; - assertFalse(activity.supportsSplitScreenWindowingMode()); - - // Force resizable - mAtm.mForceResizableActivities = true; - mAtm.mSupportsNonResizableMultiWindow = -1; - mAtm.mDevEnableNonResizableMultiWindow = false; - assertTrue(activity.supportsSplitScreenWindowingMode()); - - // Use development option to allow non-resizable - mAtm.mForceResizableActivities = false; - mAtm.mSupportsNonResizableMultiWindow = -1; - mAtm.mDevEnableNonResizableMultiWindow = true; - assertTrue(activity.supportsSplitScreenWindowingMode()); - - // Always allow non-resizable - mAtm.mForceResizableActivities = false; - mAtm.mSupportsNonResizableMultiWindow = 1; - mAtm.mDevEnableNonResizableMultiWindow = false; - assertTrue(activity.supportsSplitScreenWindowingMode()); - } - - @Test public void testSupportsFreeform() { final ActivityRecord activity = new ActivityBuilder(mAtm) .setCreateTask(true) diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index b9432753c17f..1176786eacc7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -794,7 +794,7 @@ public class ActivityStarterTests extends WindowTestsBase { // Create adjacent tasks and put one activity under it final Task parent = new TaskBuilder(mSupervisor).build(); final Task adjacentParent = new TaskBuilder(mSupervisor).build(); - parent.setAdjacentTaskFragment(adjacentParent, true); + parent.setAdjacentTaskFragment(adjacentParent); final ActivityRecord activity = new ActivityBuilder(mAtm) .setParentTask(parent) .setCreateTask(true).build(); @@ -1120,28 +1120,27 @@ public class ActivityStarterTests extends WindowTestsBase { } @Test - public void testTargetStackInSplitScreen() { + public void testTargetTaskInSplitScreen() { final ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_LAUNCH_ADJACENT, false /* mockGetRootTask */); final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true).build(); final ActivityOptions options = ActivityOptions.makeBasic(); final ActivityRecord[] outActivity = new ActivityRecord[1]; - // Activity must not land on split-screen stack if currently not in split-screen mode. + // Activity must not land on split-screen task if currently not in split-screen mode. starter.setActivityOptions(options.toBundle()) - .setReason("testWindowingModeOptionsLaunchAdjacent") + .setReason("testTargetTaskInSplitScreen") .setOutActivity(outActivity).execute(); assertThat(outActivity[0].inMultiWindowMode()).isFalse(); - // Move activity to split-screen-primary stack and make sure it has the focus. + // Move activity to split-screen-primary task and make sure it has the focus. TestSplitOrganizer splitOrg = new TestSplitOrganizer(mAtm, top.getDisplayContent()); top.getRootTask().reparent(splitOrg.mPrimary, POSITION_BOTTOM); - top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent"); + top.getRootTask().moveToFront("testTargetTaskInSplitScreen"); - // Activity must landed on split-screen-secondary when launch adjacent. - starter.setActivityOptions(options.toBundle()) - .setReason("testWindowingModeOptionsLaunchAdjacent") - .setOutActivity(outActivity).execute(); + // Activity must land on split-screen-secondary when launch adjacent. + startActivityInner(starter, outActivity[0], top, options, null /* inTask */, + null /* taskFragment*/); assertThat(outActivity[0].inMultiWindowMode()).isTrue(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index a8571906bb06..20b1120d7d3e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -21,7 +21,6 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; -import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; @@ -42,8 +41,8 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.doCallRealMethod; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.annotation.Nullable; @@ -60,7 +59,6 @@ import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; import android.os.LocaleList; -import android.os.PowerManager; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.Display; @@ -136,7 +134,7 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { assertNull(transaction.getLifecycleStateRequest()); } - @Test(expected = IllegalStateException.class) + @Test public void testOnPictureInPictureRequested_cannotEnterPip() throws RemoteException { final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); @@ -146,11 +144,16 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { mAtm.mActivityClientController.requestPictureInPictureMode(activity); - // Check enter no transactions with enter pip requests are made. - verify(lifecycleManager, times(0)).scheduleTransaction(any()); + verify(lifecycleManager, atLeast(0)) + .scheduleTransaction(mClientTransactionCaptor.capture()); + final ClientTransaction transaction = mClientTransactionCaptor.getValue(); + // Check that none are enter pip request items. + transaction.getCallbacks().forEach(clientTransactionItem -> { + assertFalse(clientTransactionItem instanceof EnterPipRequestedItem); + }); } - @Test(expected = IllegalStateException.class) + @Test public void testOnPictureInPictureRequested_alreadyInPIPMode() throws RemoteException { final Task stack = new TaskBuilder(mSupervisor).setCreateActivity(true).build(); final ActivityRecord activity = stack.getBottomMostTask().getTopNonFinishingActivity(); @@ -159,8 +162,13 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { mAtm.mActivityClientController.requestPictureInPictureMode(activity); - // Check that no transactions with enter pip requests are made. - verify(lifecycleManager, times(0)).scheduleTransaction(any()); + verify(lifecycleManager, atLeast(0)) + .scheduleTransaction(mClientTransactionCaptor.capture()); + final ClientTransaction transaction = mClientTransactionCaptor.getValue(); + // Check that none are enter pip request items. + transaction.getCallbacks().forEach(clientTransactionItem -> { + assertFalse(clientTransactionItem instanceof EnterPipRequestedItem); + }); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java index 716612c70aef..75ecfd870eb2 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java @@ -21,6 +21,7 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset; @@ -34,6 +35,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -284,10 +286,14 @@ public class ActivityTaskSupervisorTests extends WindowTestsBase { .setCreateActivity(true).build().getTopMostActivity(); activity2.getTask().setResumedActivity(activity2, "test"); - mAtm.mAmInternal.deletePendingTopUid(activity1.getUid(), Long.MAX_VALUE); + final int[] pendingTopUid = new int[1]; + doAnswer(invocation -> { + pendingTopUid[0] = invocation.getArgument(0); + return null; + }).when(mAtm.mAmInternal).addPendingTopUid(anyInt(), anyInt(), any()); clearInvocations(mAtm); activity1.moveFocusableActivityToTop("test"); - assertTrue(mAtm.mAmInternal.isPendingTopUid(activity1.getUid())); + assertEquals(activity1.getUid(), pendingTopUid[0]); verify(mAtm).updateOomAdj(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 77f884c93682..8656a4fecef1 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; @@ -25,6 +26,8 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; import static android.view.WindowManager.TRANSIT_CHANGE; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN; +import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE; +import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN; import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE; import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE; @@ -155,6 +158,32 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test + public void testDreamActivityOpenTransition() { + final ActivityRecord dreamActivity = createActivityRecord(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM); + mDisplayContent.prepareAppTransition(TRANSIT_OPEN); + mDisplayContent.mOpeningApps.add(dreamActivity); + + assertEquals(TRANSIT_OLD_DREAM_ACTIVITY_OPEN, + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + mDisplayContent.mChangingContainers, null, null, false)); + } + + @Test + public void testDreamActivityCloseTransition() { + final ActivityRecord dreamActivity = createActivityRecord(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM); + mDisplayContent.prepareAppTransition(TRANSIT_CLOSE); + mDisplayContent.mClosingApps.add(dreamActivity); + + assertEquals(TRANSIT_OLD_DREAM_ACTIVITY_CLOSE, + AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition, + mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps, + mDisplayContent.mChangingContainers, null, null, false)); + } + + @Test public void testChangeIsNotOverwritten() { final ActivityRecord behind = createActivityRecord(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); @@ -564,7 +593,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { .setCreatedByOrganizer(true); final Task splitRoot1 = builder.build(); final Task splitRoot2 = builder.build(); - splitRoot1.setAdjacentTaskFragment(splitRoot2, false /* moveTogether */); + splitRoot1.setAdjacentTaskFragment(splitRoot2); final ActivityRecord activity1 = createActivityRecordWithParentTask(splitRoot1); activity1.setVisible(false); activity1.mVisibleRequested = true; diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 07923005c45e..b27060872baa 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED; +import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; @@ -123,8 +124,8 @@ import android.view.ContentRecordingSession; import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Gravity; -import android.view.IDisplayWindowRotationCallback; -import android.view.IDisplayWindowRotationController; +import android.view.IDisplayChangeWindowCallback; +import android.view.IDisplayChangeWindowController; import android.view.ISystemGestureExclusionListener; import android.view.IWindowManager; import android.view.InsetsState; @@ -135,6 +136,7 @@ import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; import android.view.View; import android.view.WindowManager; +import android.window.DisplayAreaInfo; import android.window.IDisplayAreaOrganizer; import android.window.WindowContainerToken; @@ -1097,6 +1099,25 @@ public class DisplayContentTests extends WindowTestsBase { } @Test + public void testOrientationBehind() { + final ActivityRecord prev = new ActivityBuilder(mAtm).setCreateTask(true) + .setScreenOrientation(getRotatedOrientation(mDisplayContent)).build(); + prev.mVisibleRequested = false; + final ActivityRecord top = new ActivityBuilder(mAtm).setCreateTask(true) + .setScreenOrientation(SCREEN_ORIENTATION_BEHIND).build(); + assertNotEquals(WindowConfiguration.ROTATION_UNDEFINED, + mDisplayContent.rotationForActivityInDifferentOrientation(top)); + + mDisplayContent.requestTransitionAndLegacyPrepare(WindowManager.TRANSIT_OPEN, 0); + top.setVisibility(true); + mDisplayContent.updateOrientation(); + // The top uses "behind", so the orientation is decided by the previous. + assertEquals(prev, mDisplayContent.getLastOrientationSource()); + // The top will use the rotation from "prev" with fixed rotation. + assertTrue(top.hasFixedRotationTransform()); + } + + @Test public void testFixedToUserRotationChanged() { final DisplayContent dc = createNewDisplay(); dc.getDisplayRotation().setFixedToUserRotation( @@ -1693,8 +1714,7 @@ public class DisplayContentTests extends WindowTestsBase { // The condition should reject using fixed rotation because the resumed client in real case // might get display info immediately. And the fixed rotation adjustments haven't arrived // client side so the info may be inconsistent with the requested orientation. - verify(mDisplayContent).handleTopActivityLaunchingInDifferentOrientation(eq(app), - eq(true) /* checkOpening */); + verify(mDisplayContent).updateOrientation(eq(app), anyBoolean()); assertFalse(app.isFixedRotationTransforming()); assertFalse(mDisplayContent.hasTopFixedRotationLaunchingApp()); } @@ -1782,15 +1802,16 @@ public class DisplayContentTests extends WindowTestsBase { return true; }).when(dc).updateDisplayOverrideConfigurationLocked(); final boolean[] called = new boolean[1]; - mWm.mDisplayRotationController = - new IDisplayWindowRotationController.Stub() { + mWm.mDisplayChangeController = + new IDisplayChangeWindowController.Stub() { @Override - public void onRotateDisplay(int displayId, int fromRotation, int toRotation, - IDisplayWindowRotationCallback callback) { + public void onDisplayChange(int displayId, int fromRotation, int toRotation, + DisplayAreaInfo newDisplayAreaInfo, + IDisplayChangeWindowCallback callback) throws RemoteException { called[0] = true; try { - callback.continueRotateDisplay(toRotation, null); + callback.continueDisplayChange(null); } catch (RemoteException e) { assertTrue(false); } @@ -1824,13 +1845,14 @@ public class DisplayContentTests extends WindowTestsBase { // Rotate 180 degree so the display doesn't have configuration change. This condition is // used for the later verification of stop-freezing (without setting mWaitingForConfig). doReturn((dr.getRotation() + 2) % 4).when(dr).rotationForOrientation(anyInt(), anyInt()); - mWm.mDisplayRotationController = - new IDisplayWindowRotationController.Stub() { + mWm.mDisplayChangeController = + new IDisplayChangeWindowController.Stub() { @Override - public void onRotateDisplay(int displayId, int fromRotation, int toRotation, - IDisplayWindowRotationCallback callback) { + public void onDisplayChange(int displayId, int fromRotation, int toRotation, + DisplayAreaInfo newDisplayAreaInfo, + IDisplayChangeWindowCallback callback) throws RemoteException { try { - callback.continueRotateDisplay(toRotation, null); + callback.continueDisplayChange(null); } catch (RemoteException e) { assertTrue(false); } @@ -2193,23 +2215,28 @@ public class DisplayContentTests extends WindowTestsBase { */ @Test public void testCreateTestDisplayContentFromDimensions() { - final int displayWidth = 1000; - final int displayHeight = 2000; + final int displayWidth = 540; + final int displayHeight = 960; + final int density = 192; + final int expectedWidthDp = 450; // = 540/(192/160) + final int expectedHeightDp = 800; // = 960/(192/160) final int windowingMode = WINDOWING_MODE_FULLSCREEN; final boolean ignoreOrientationRequests = false; final float fixedOrientationLetterboxRatio = 0; final DisplayContent testDisplayContent = new TestDisplayContent.Builder(mAtm, displayWidth, - displayHeight).build(); + displayHeight).setDensityDpi(density).build(); // test display info final DisplayInfo di = testDisplayContent.getDisplayInfo(); assertEquals(displayWidth, di.logicalWidth); assertEquals(displayHeight, di.logicalHeight); - assertEquals(TestDisplayContent.DEFAULT_LOGICAL_DISPLAY_DENSITY, di.logicalDensityDpi); + assertEquals(density, di.logicalDensityDpi); // test configuration - final WindowConfiguration windowConfig = testDisplayContent.getConfiguration() - .windowConfiguration; + final Configuration config = testDisplayContent.getConfiguration(); + assertEquals(expectedWidthDp, config.screenWidthDp); + assertEquals(expectedHeightDp, config.screenHeightDp); + final WindowConfiguration windowConfig = config.windowConfiguration; assertEquals(displayWidth, windowConfig.getBounds().width()); assertEquals(displayHeight, windowConfig.getBounds().height()); assertEquals(windowingMode, windowConfig.getWindowingMode()); diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java index dbb7fae548b7..db3a51ca4791 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java @@ -39,6 +39,7 @@ import static com.android.server.wm.SizeCompatTests.rotateDisplay; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -168,7 +169,8 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { mSecondRoot.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); mDisplay.onLastFocusedTaskDisplayAreaChanged(mFirstTda); - prepareUnresizable(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT); + prepareLimitedBounds(mFirstActivity, SCREEN_ORIENTATION_PORTRAIT, + false /* isUnresizable */); final Rect dagBounds = new Rect(mFirstRoot.getBounds()); final Rect taskBounds = new Rect(mFirstTask.getBounds()); final Rect activityBounds = new Rect(mFirstActivity.getBounds()); @@ -209,8 +211,10 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(activityConfigBounds.width()).isEqualTo(activityBounds.width()); assertThat(activityConfigBounds.height()).isEqualTo(activityBounds.height()); assertThat(activitySizeCompatBounds.height()).isEqualTo(newTaskBounds.height()); - assertThat(activitySizeCompatBounds.width()).isEqualTo( - newTaskBounds.height() * newTaskBounds.height() / newTaskBounds.width()); + final float defaultAspectRatio = mFirstActivity.mWmService.mLetterboxConfiguration + .getDefaultMinAspectRatioForUnresizableApps(); + assertEquals(activitySizeCompatBounds.width(), + newTaskBounds.height() / defaultAspectRatio, 0.5); } @Test @@ -230,8 +234,9 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase { assertThat(mFirstActivity.inSizeCompatMode()).isFalse(); assertThat(taskBounds).isEqualTo(dagBounds); assertThat(activityBounds.width()).isEqualTo(dagBounds.width()); - assertThat(activityBounds.height()) - .isEqualTo(dagBounds.width() * dagBounds.width() / dagBounds.height()); + final float defaultAspectRatio = mFirstActivity.mWmService.mLetterboxConfiguration + .getDefaultMinAspectRatioForUnresizableApps(); + assertEquals(activityBounds.height(), dagBounds.width() / defaultAspectRatio, 0.5); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java index 1e86522a2307..e502f2fbd173 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java @@ -63,7 +63,7 @@ public class LetterboxTest { mLetterbox = new Letterbox(mSurfaces, StubTransaction::new, () -> mAreCornersRounded, () -> Color.valueOf(mColor), () -> mHasWallpaperBackground, () -> mBlurRadius, () -> mDarkScrimAlpha, - /* doubleTapCallback= */ x -> {}); + /* doubleTapCallbackX= */ x -> {}, /* doubleTapCallbackY= */ y -> {}); mTransaction = spy(StubTransaction.class); } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index feb1c6fa6b5c..1708ed7d4686 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -1229,7 +1229,6 @@ public class RecentTasksTest extends WindowTestsBase { RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true); assertTrue(info.supportsMultiWindow); - assertTrue(info.supportsSplitScreenMultiWindow); // The task can be put in split screen even if it is not attached now. task.removeImmediately(); @@ -1237,7 +1236,6 @@ public class RecentTasksTest extends WindowTestsBase { info = mRecentTasks.createRecentTaskInfo(task, true); assertTrue(info.supportsMultiWindow); - assertTrue(info.supportsSplitScreenMultiWindow); // Test non-resizable. // The non-resizable task cannot be put in split screen because of the config. @@ -1247,7 +1245,6 @@ public class RecentTasksTest extends WindowTestsBase { info = mRecentTasks.createRecentTaskInfo(task, true); assertFalse(info.supportsMultiWindow); - assertFalse(info.supportsSplitScreenMultiWindow); // Even if it is not attached, the non-resizable task can be put in split screen as long as // the device supports it. @@ -1256,8 +1253,6 @@ public class RecentTasksTest extends WindowTestsBase { info = mRecentTasks.createRecentTaskInfo(task, true); assertTrue(info.supportsMultiWindow); - assertTrue(info.supportsSplitScreenMultiWindow); - } private TaskSnapshot createSnapshot(Point taskSize, Point bufferSize) { diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 700fadd61c9a..68079f4a9ac5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -657,7 +657,7 @@ public class RootWindowContainerTests extends WindowTestsBase { doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); - mAtm.setBooted(true); + setBooted(mAtm); // Trigger resume on all displays mRootWindowContainer.resumeFocusedTasksTopActivities(); @@ -685,7 +685,7 @@ public class RootWindowContainerTests extends WindowTestsBase { doReturn(true).when(mRootWindowContainer).resumeHomeActivity(any(), any(), any()); - mAtm.setBooted(true); + setBooted(mAtm); // Trigger resume on all displays mRootWindowContainer.resumeFocusedTasksTopActivities(); @@ -771,17 +771,10 @@ public class RootWindowContainerTests extends WindowTestsBase { @Test public void testNotStartHomeBeforeBoot() { final int displayId = 1; - final boolean isBooting = mAtm.mAmInternal.isBooting(); - final boolean isBooted = mAtm.mAmInternal.isBooted(); - try { - mAtm.mAmInternal.setBooting(false); - mAtm.mAmInternal.setBooted(false); - mRootWindowContainer.onDisplayAdded(displayId); - verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); - } finally { - mAtm.mAmInternal.setBooting(isBooting); - mAtm.mAmInternal.setBooted(isBooted); - } + doReturn(false).when(mAtm).isBooting(); + doReturn(false).when(mAtm).isBooted(); + mRootWindowContainer.onDisplayAdded(displayId); + verify(mRootWindowContainer, never()).startHomeOnDisplay(anyInt(), any(), anyInt()); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index 891b33baf2f1..7f70882a70fc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -1259,6 +1259,36 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO, + ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE}) + public void testOverrideMinAspectRatioLargeForResizableAppInSplitScreen() { + setUpDisplaySizeWithApp(/* dw= */ 1000, /* dh= */ 2800); + + // Create a size compat activity on the same task. + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(mTask) + .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) + .setComponent(ComponentName.createRelative(mContext, + SizeCompatTests.class.getName())) + .setUid(android.os.Process.myUid()) + .build(); + + final TestSplitOrganizer organizer = + new TestSplitOrganizer(mAtm, activity.getDisplayContent()); + + // Move activity to split screen which takes half of the screen. + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + organizer.mPrimary.setBounds(0, 0, 1000, 1400); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode()); + + // The per-package override forces the activity into a 16:9 aspect ratio + assertEquals(1400, activity.getBounds().height()); + assertEquals(1400 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, + activity.getBounds().width(), 0.5); + } + + @Test public void testLaunchWithFixedRotationTransform() { final int dw = 1000; final int dh = 2500; @@ -1409,12 +1439,10 @@ public class SizeCompatTests extends WindowTestsBase { setUpDisplaySizeWithApp(2800, 1400); mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); - // Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed - // orientation letterbox. final float fixedOrientationLetterboxAspectRatio = 1.1f; mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio( fixedOrientationLetterboxAspectRatio); - prepareUnresizable(mActivity, 0, SCREEN_ORIENTATION_PORTRAIT); + prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, /* isUnresizable= */ false); final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); final Rect activityBounds = new Rect(mActivity.getBounds()); @@ -1435,6 +1463,103 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testDisplayIgnoreOrientationRequest_unresizableWithCorrespondingMinAspectRatio() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + final float fixedOrientationLetterboxAspectRatio = 1.1f; + mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio( + fixedOrientationLetterboxAspectRatio); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + + final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + final Rect activityBounds = new Rect(mActivity.getBounds()); + + // Display shouldn't be rotated. + assertEquals(SCREEN_ORIENTATION_UNSPECIFIED, + mActivity.mDisplayContent.getLastOrientation()); + assertTrue(displayBounds.width() > displayBounds.height()); + + // App should launch in fixed orientation letterbox. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); + assertFalse(mActivity.inSizeCompatMode()); + + // Letterbox logic should use config_letterboxDefaultMinAspectRatioForUnresizableApps over + // config_fixedOrientationLetterboxAspectRatio. + assertEquals(displayBounds.height(), activityBounds.height()); + final float defaultAspectRatio = mActivity.mWmService.mLetterboxConfiguration + .getDefaultMinAspectRatioForUnresizableApps(); + assertEquals(displayBounds.height() / defaultAspectRatio, activityBounds.width(), 0.5); + } + + @Test + public void testSplitAspectRatioForUnresizablePortraitApps() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(1600, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mActivity.mWmService.mLetterboxConfiguration + .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true); + + mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f); + + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + + final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + final Rect activityBounds = new Rect(mActivity.getBounds()); + + // App should launch in fixed orientation letterbox. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); + // Checking that there is no size compat mode. + assertFitted(); + + assertEquals(displayBounds.height(), activityBounds.height()); + assertTrue(activityBounds.width() < displayBounds.width() / 2); + + final TestSplitOrganizer organizer = + new TestSplitOrganizer(mAtm, mActivity.getDisplayContent()); + // Move activity to split screen which takes half of the screen. + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); + // Checking that there is no size compat mode. + assertFitted(); + } + + @Test + public void testSplitAspectRatioForUnresizableLandscapeApps() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(1400, 1600); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + mActivity.mWmService.mLetterboxConfiguration + .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(true); + + mActivity.mWmService.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(1.1f); + + prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE); + + final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds()); + final Rect activityBounds = new Rect(mActivity.getBounds()); + + // App should launch in fixed orientation letterbox. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); + // Checking that there is no size compat mode. + assertFitted(); + + assertEquals(displayBounds.width(), activityBounds.width()); + assertTrue(activityBounds.height() < displayBounds.height() / 2); + + final TestSplitOrganizer organizer = + new TestSplitOrganizer(mAtm, mActivity.getDisplayContent()); + // Move activity to split screen which takes half of the screen. + mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents= */ false , "test"); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, mTask.getWindowingMode()); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode()); + // Checking that there is no size compat mode. + assertFitted(); + } + + @Test public void testDisplayIgnoreOrientationRequest_orientationLetterboxBecameSizeCompatAfterRotate() { // Set up a display in landscape and ignoring orientation request. @@ -1908,7 +2033,7 @@ public class SizeCompatTests extends WindowTestsBase { } @Test - public void testSupportsNonResizableInSplitScreen_fillTaskForSameOrientation() { + public void testSupportsNonResizableInSplitScreen_aspectRatioLetterboxInSameOrientation() { // Support non resizable in multi window mAtm.mDevEnableNonResizableMultiWindow = true; setUpDisplaySizeWithApp(1000, 2800); @@ -1946,7 +2071,12 @@ public class SizeCompatTests extends WindowTestsBase { // Activity bounds fill split screen. final Rect primarySplitBounds = new Rect(organizer.mPrimary.getBounds()); final Rect letterboxedBounds = new Rect(mActivity.getBounds()); - assertEquals(primarySplitBounds, letterboxedBounds); + // Activity is letterboxed for aspect ratio. + assertEquals(primarySplitBounds.height(), letterboxedBounds.height()); + final float defaultAspectRatio = mActivity.mWmService.mLetterboxConfiguration + .getDefaultMinAspectRatioForUnresizableApps(); + assertEquals(primarySplitBounds.height() / defaultAspectRatio, + letterboxedBounds.width(), 0.5); } @Test @@ -1960,7 +2090,7 @@ public class SizeCompatTests extends WindowTestsBase { prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED); // Bounds are letterboxed to respect the provided max aspect ratio. - assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 1100)); + assertEquals(mActivity.getBounds(), new Rect(0, 850, 1000, 1950)); // Move activity to split screen which has landscape size. mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents */ false, "test"); @@ -2077,19 +2207,14 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier( letterboxHorizontalPositionMultiplier); prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); - assertEquals(fixedOrientationLetterbox, mActivity.getBounds()); - // Rotate to put activity in size compat mode. rotateDisplay(mActivity.mDisplayContent, ROTATION_90); - assertTrue(mActivity.inSizeCompatMode()); // Activity is in size compat mode but not scaled. assertEquals(sizeCompatUnscaled, mActivity.getBounds()); - // Force activity to scaled down for size compat mode. resizeDisplay(mTask.mDisplayContent, 700, 1400); - assertTrue(mActivity.inSizeCompatMode()); assertScaled(); assertEquals(sizeCompatScaled, mActivity.getBounds()); @@ -2107,6 +2232,109 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testUpdateResolvedBoundsVerticalPosition_top() { + // Display configured as (1400, 2800). + assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + /* letterboxVerticalPositionMultiplier */ 0.0f, + // At launch. + /* fixedOrientationLetterbox */ new Rect(0, 0, 1400, 700), + // After 90 degree rotation. + /* sizeCompatUnscaled */ new Rect(700, 0, 2100, 700), + // After the display is resized to (1400, 700). + /* sizeCompatScaled */ new Rect(0, 0, 700, 350)); + } + + @Test + public void testUpdateResolvedBoundsVerticalPosition_center() { + // Display configured as (1400, 2800). + assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + /* letterboxVerticalPositionMultiplier */ 0.5f, + // At launch. + /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750), + // After 90 degree rotation. + /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050), + // After the display is resized to (1400, 700). + /* sizeCompatScaled */ new Rect(0, 525, 700, 875)); + } + + @Test + public void testUpdateResolvedBoundsVerticalPosition_invalidMultiplier_defaultToCenter() { + // Display configured as (1400, 2800). + + // Below 0.0. + assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + /* letterboxVerticalPositionMultiplier */ -1.0f, + // At launch. + /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750), + // After 90 degree rotation. + /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050), + // After the display is resized to (1400, 700). + /* sizeCompatScaled */ new Rect(0, 525, 700, 875)); + + // Above 1.0 + assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + /* letterboxVerticalPositionMultiplier */ 2.0f, + // At launch. + /* fixedOrientationLetterbox */ new Rect(0, 1050, 1400, 1750), + // After 90 degree rotation. + /* sizeCompatUnscaled */ new Rect(700, 350, 2100, 1050), + // After the display is resized to (1400, 700). + /* sizeCompatScaled */ new Rect(0, 525, 700, 875)); + } + + @Test + public void testUpdateResolvedBoundsVerticalPosition_bottom() { + // Display configured as (1400, 2800). + assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + /* letterboxVerticalPositionMultiplier */ 1.0f, + // At launch. + /* fixedOrientationLetterbox */ new Rect(0, 2100, 1400, 2800), + // After 90 degree rotation. + /* sizeCompatUnscaled */ new Rect(700, 700, 2100, 1400), + // After the display is resized to (1400, 700). + /* sizeCompatScaled */ new Rect(0, 1050, 700, 1400)); + } + + private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity( + float letterboxVerticalPositionMultiplier, Rect fixedOrientationLetterbox, + Rect sizeCompatUnscaled, Rect sizeCompatScaled) { + // Set up a display in portrait and ignoring orientation request. + setUpDisplaySizeWithApp(1400, 2800); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier( + letterboxVerticalPositionMultiplier); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE); + + assertEquals(fixedOrientationLetterbox, mActivity.getBounds()); + + // Rotate to put activity in size compat mode. + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + + assertTrue(mActivity.inSizeCompatMode()); + // Activity is in size compat mode but not scaled. + assertEquals(sizeCompatUnscaled, mActivity.getBounds()); + + // Force activity to scaled down for size compat mode. + resizeDisplay(mTask.mDisplayContent, 1400, 700); + + assertTrue(mActivity.inSizeCompatMode()); + assertScaled(); + assertEquals(sizeCompatScaled, mActivity.getBounds()); + } + + @Test + public void testUpdateResolvedBoundsVerticalPosition_activityFillParentHeight() { + // When activity height equals parent height, multiplier shouldn't have any effect. + assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity( + /* letterboxVerticalPositionMultiplier */ 0.0f); + assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity( + /* letterboxVerticalPositionMultiplier */ 0.5f); + assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity( + /* letterboxVerticalPositionMultiplier */ 1.0f); + } + + @Test public void testAreBoundsLetterboxed_letterboxedForAspectRatio_returnsTrue() { setUpDisplaySizeWithApp(1000, 2500); @@ -2399,6 +2627,23 @@ public class SizeCompatTests extends WindowTestsBase { mActivity.mWmService.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier( letterboxHorizontalPositionMultiplier); prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE); + assertFitted(); + // Rotate to put activity in size compat mode. + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + assertTrue(mActivity.inSizeCompatMode()); + // Activity is in size compat mode but not scaled. + assertEquals(new Rect(0, 1050, 1400, 1750), mActivity.getBounds()); + } + + private void assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity( + float letterboxVerticalPositionMultiplier) { + // Set up a display in portrait and ignoring orientation request. + setUpDisplaySizeWithApp(1400, 2800); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + mActivity.mWmService.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier( + letterboxVerticalPositionMultiplier); + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); assertFitted(); @@ -2407,7 +2652,7 @@ public class SizeCompatTests extends WindowTestsBase { assertTrue(mActivity.inSizeCompatMode()); // Activity is in size compat mode but not scaled. - assertEquals(new Rect(0, 1050, 1400, 1750), mActivity.getBounds()); + assertEquals(new Rect(1050, 0, 1750, 1400), mActivity.getBounds()); } private static WindowState addWindowToActivity(ActivityRecord activity) { diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java index dc9a62554fdb..18c4eb964a3a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java +++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java @@ -66,7 +66,6 @@ import android.os.UserHandle; import android.provider.DeviceConfig; import android.util.Log; import android.view.InputChannel; -import android.view.Surface; import android.view.SurfaceControl; import com.android.dx.mockito.inline.extended.StaticMockitoSession; @@ -77,7 +76,6 @@ import com.android.server.LockGuard; import com.android.server.UiThread; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService; -import com.android.server.appop.AppOpsService; import com.android.server.display.color.ColorDisplayService; import com.android.server.firewall.IntentFirewall; import com.android.server.input.InputManagerService; @@ -94,10 +92,8 @@ import org.mockito.MockSettings; import org.mockito.Mockito; import org.mockito.quality.Strictness; -import java.io.File; import java.util.ArrayList; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; /** * JUnit test rule to correctly setting up system services like {@link WindowManagerService} @@ -125,13 +121,11 @@ public class SystemServicesTestRule implements TestRule { private Description mDescription; private Context mContext; private StaticMockitoSession mMockitoSession; - private ActivityManagerService mAmService; private ActivityTaskManagerService mAtmService; private WindowManagerService mWmService; private WindowState.PowerManagerWrapper mPowerManagerWrapper; private InputManagerService mImService; private InputChannel mInputChannel; - private Supplier<Surface> mSurfaceFactory = () -> mock(Surface.class); /** * Spied {@link SurfaceControl.Transaction} class than can be used to verify calls. */ @@ -289,29 +283,9 @@ public class SystemServicesTestRule implements TestRule { } private void setUpActivityTaskManagerService() { - // ActivityManagerService - mAmService = new ActivityManagerService(new AMTestInjector(mContext), null /* thread */); - spyOn(mAmService); - doReturn(mock(IPackageManager.class)).when(mAmService).getPackageManager(); - doNothing().when(mAmService).grantImplicitAccess( - anyInt(), any(), anyInt(), anyInt()); - // ActivityManagerInternal - final ActivityManagerInternal amInternal = mAmService.mInternal; - spyOn(amInternal); - doNothing().when(amInternal).trimApplications(); - doNothing().when(amInternal).scheduleAppGcs(); - doNothing().when(amInternal).updateCpuStats(); - doNothing().when(amInternal).updateOomAdj(); - doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean()); - doNothing().when(amInternal).updateActivityUsageStats( - any(), anyInt(), anyInt(), any(), any()); - doNothing().when(amInternal).startProcess( - any(), any(), anyBoolean(), anyBoolean(), any(), any()); - doNothing().when(amInternal).updateOomLevelsForDisplay(anyInt()); - doNothing().when(amInternal).broadcastGlobalConfigurationChanged(anyInt(), anyBoolean()); - doNothing().when(amInternal).cleanUpServices(anyInt(), any(), any()); - doNothing().when(amInternal).reportCurKeyguardUsageEvent(anyBoolean()); + final ActivityManagerInternal amInternal = + mock(ActivityManagerInternal.class, withSettings().stubOnly()); doReturn(UserHandle.USER_SYSTEM).when(amInternal).getCurrentUserId(); doReturn(TEST_USER_PROFILE_IDS).when(amInternal).getCurrentProfileIds(); doReturn(true).when(amInternal).isUserRunning(anyInt(), anyInt()); @@ -320,7 +294,9 @@ public class SystemServicesTestRule implements TestRule { doReturn(false).when(amInternal).isActivityStartsLoggingEnabled(); LocalServices.addService(ActivityManagerInternal.class, amInternal); - mAtmService = new TestActivityTaskManagerService(mContext, mAmService); + final ActivityManagerService amService = + mock(ActivityManagerService.class, withSettings().stubOnly()); + mAtmService = new TestActivityTaskManagerService(mContext, amService); LocalServices.addService(ActivityTaskManagerInternal.class, mAtmService.getAtmInternal()); } @@ -334,7 +310,7 @@ public class SystemServicesTestRule implements TestRule { mWmService = WindowManagerService.main( mContext, mImService, false, false, wmPolicy, mAtmService, testDisplayWindowSettingsProvider, StubTransaction::new, - () -> mSurfaceFactory.get(), (unused) -> new MockSurfaceControlBuilder()); + (unused) -> new MockSurfaceControlBuilder()); spyOn(mWmService); spyOn(mWmService.mRoot); // Invoked during {@link ActivityStack} creation. @@ -355,7 +331,7 @@ public class SystemServicesTestRule implements TestRule { null, null, mTransaction, mWmService.mPowerManagerInternal); mWmService.onInitReady(); - mAmService.setWindowManager(mWmService); + mAtmService.setWindowManager(mWmService); mWmService.mDisplayEnabled = true; mWmService.mDisplayReady = true; // Set configuration for default display @@ -454,10 +430,6 @@ public class SystemServicesTestRule implements TestRule { .spiedInstance(sWakeLock).stubOnly()); } - void setSurfaceFactory(Supplier<Surface> factory) { - mSurfaceFactory = factory; - } - void cleanupWindowManagerHandlers() { final WindowManagerService wm = getWindowManagerService(); if (wm == null) { @@ -618,32 +590,4 @@ public class SystemServicesTestRule implements TestRule { doReturn(true).when(controller).checkKeyguardVisibility(any()); } } - - // TODO: Can we just mock this? - private static class AMTestInjector extends ActivityManagerService.Injector { - - AMTestInjector(Context context) { - super(context); - } - - @Override - public Context getContext() { - return getInstrumentation().getTargetContext(); - } - - @Override - public AppOpsService getAppOpsService(File file, Handler handler) { - return null; - } - - @Override - public Handler getUiHandler(ActivityManagerService service) { - return UiThread.getHandler(); - } - - @Override - public boolean isNetworkRestrictedForUid(int uid) { - return false; - } - } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index e8f1d2390c34..2a9fcb9d070b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -82,7 +82,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); adjacentRootTask.mCreatedByOrganizer = true; final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); - adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */); + adjacentRootTask.setAdjacentTaskFragment(rootTask); taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask); Task actualRootTask = taskDisplayArea.getLaunchRootTask( @@ -108,7 +108,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { final Task adjacentRootTask = createTask( mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); adjacentRootTask.mCreatedByOrganizer = true; - adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */); + adjacentRootTask.setAdjacentTaskFragment(rootTask); taskDisplayArea.setLaunchRootTask(rootTask, new int[]{WINDOWING_MODE_MULTI_WINDOW}, new int[]{ACTIVITY_TYPE_STANDARD}); @@ -129,7 +129,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); adjacentRootTask.mCreatedByOrganizer = true; final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); - adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */); + adjacentRootTask.setAdjacentTaskFragment(rootTask); taskDisplayArea.setLaunchAdjacentFlagRootTask(adjacentRootTask); final Task actualRootTask = taskDisplayArea.getLaunchRootTask( @@ -756,7 +756,7 @@ public class TaskDisplayAreaTests extends WindowTestsBase { adjacentRootTask.mCreatedByOrganizer = true; final Task candidateTask = createTaskInRootTask(rootTask, 0 /* userId*/); final TaskDisplayArea taskDisplayArea = rootTask.getDisplayArea(); - adjacentRootTask.setAdjacentTaskFragment(rootTask, false /* moveTogether */); + adjacentRootTask.setAdjacentTaskFragment(rootTask); // Verify the launch root with candidate task Task actualRootTask = taskDisplayArea.getLaunchRootTask(WINDOWING_MODE_UNDEFINED, diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 5340a79f4b94..3ec24b76960f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -24,6 +24,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.WindowContainer.POSITION_TOP; import static com.android.server.wm.testing.Assert.assertThrows; +import static com.google.common.truth.Truth.assertWithMessage; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -32,7 +34,6 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; @@ -43,6 +44,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; @@ -78,6 +80,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { private static final int TASK_ID = 10; private TaskFragmentOrganizerController mController; + private WindowOrganizerController mWindowOrganizerController; private TaskFragmentOrganizer mOrganizer; private TaskFragmentOrganizerToken mOrganizerToken; private ITaskFragmentOrganizer mIOrganizer; @@ -87,10 +90,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { private WindowContainerTransaction mTransaction; private WindowContainerToken mFragmentWindowToken; private RemoteAnimationDefinition mDefinition; + private IBinder mErrorToken; + private Rect mTaskFragBounds; @Before public void setup() { - mController = mAtm.mWindowOrganizerController.mTaskFragmentOrganizerController; + mWindowOrganizerController = mAtm.mWindowOrganizerController; + mController = mWindowOrganizerController.mTaskFragmentOrganizerController; mOrganizer = new TaskFragmentOrganizer(Runnable::run); mOrganizerToken = mOrganizer.getOrganizerToken(); mIOrganizer = ITaskFragmentOrganizer.Stub.asInterface(mOrganizerToken.asBinder()); @@ -101,6 +107,10 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { mTransaction = new WindowContainerTransaction(); mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken(); mDefinition = new RemoteAnimationDefinition(); + mErrorToken = new Binder(); + final Rect displayBounds = mDisplayContent.getBounds(); + mTaskFragBounds = new Rect(displayBounds.left, displayBounds.top, displayBounds.centerX(), + displayBounds.centerY()); spyOn(mController); spyOn(mOrganizer); @@ -221,16 +231,15 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test - public void testOnTaskFragmentError() throws RemoteException { - final IBinder errorCallbackToken = new Binder(); + public void testOnTaskFragmentError() { final Throwable exception = new IllegalArgumentException("Test exception"); mController.registerOrganizer(mIOrganizer); mController.onTaskFragmentError(mTaskFragment.getTaskFragmentOrganizer(), - errorCallbackToken, exception); + mErrorToken, exception); mController.dispatchPendingEvents(); - verify(mOrganizer).onTaskFragmentError(eq(errorCallbackToken), eq(exception)); + verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), eq(exception)); } @Test @@ -280,7 +289,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final int uid = Binder.getCallingUid(); mTaskFragment.setTaskFragmentOrganizer(mOrganizer.getOrganizerToken(), uid, DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME); - mAtm.mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); + mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); mController.registerOrganizer(mIOrganizer); mOrganizer.applyTransaction(mTransaction); final Task task = createTask(mDisplayContent); @@ -305,7 +314,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final IBinder temporaryToken = token.getValue(); assertNotEquals(activity.token, temporaryToken); mTransaction.reparentActivityToTaskFragment(mFragmentToken, temporaryToken); - mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + mWindowOrganizerController.applyTransaction(mTransaction); assertEquals(mTaskFragment, activity.getTaskFragment()); // The temporary token can only be used once. @@ -395,8 +404,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { // No lifecycle update when the TaskFragment is not recorded. verify(mAtm.mRootWindowContainer, never()).resumeFocusedTasksTopActivities(); - mAtm.mWindowOrganizerController.mLaunchTaskFragments - .put(mFragmentToken, mTaskFragment); + mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); assertApplyTransactionAllowed(mTransaction); verify(mAtm.mRootWindowContainer).resumeFocusedTasksTopActivities(); @@ -412,7 +420,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { // Throw exception if the transaction is trying to change a window that is not organized by // the organizer. - mTransaction.setAdjacentRoots(mFragmentWindowToken, token2, false /* moveTogether */); + mTransaction.setAdjacentRoots(mFragmentWindowToken, token2); assertApplyTransactionDisallowed(mTransaction); @@ -466,23 +474,23 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { // Fail to create TaskFragment when the task uid is different from caller. activity.info.applicationInfo.uid = uid; activity.getTask().effectiveUid = uid + 1; - mAtm.getWindowOrganizerController().applyTransaction(mTransaction); + mWindowOrganizerController.applyTransaction(mTransaction); - assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); + assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); // Fail to create TaskFragment when the task uid is different from owner activity. activity.info.applicationInfo.uid = uid + 1; activity.getTask().effectiveUid = uid; - mAtm.getWindowOrganizerController().applyTransaction(mTransaction); + mWindowOrganizerController.applyTransaction(mTransaction); - assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); + assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); // Successfully created a TaskFragment for same uid. activity.info.applicationInfo.uid = uid; activity.getTask().effectiveUid = uid; - mAtm.getWindowOrganizerController().applyTransaction(mTransaction); + mWindowOrganizerController.applyTransaction(mTransaction); - assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); + assertNotNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); } @Test @@ -519,7 +527,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { .setParentTask(task) .setFragmentToken(mFragmentToken) .build(); - mAtm.mWindowOrganizerController.mLaunchTaskFragments + mWindowOrganizerController.mLaunchTaskFragments .put(mFragmentToken, mTaskFragment); mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token); doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity); @@ -549,8 +557,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { .setOrganizer(mOrganizer) .createActivityCount(1) .build(); - mAtm.mWindowOrganizerController.mLaunchTaskFragments.put(token0, tf0); - mAtm.mWindowOrganizerController.mLaunchTaskFragments.put(token1, tf1); + mWindowOrganizerController.mLaunchTaskFragments.put(token0, tf0); + mWindowOrganizerController.mLaunchTaskFragments.put(token1, tf1); final ActivityRecord activity0 = tf0.getTopMostActivity(); final ActivityRecord activity1 = tf1.getTopMostActivity(); @@ -558,7 +566,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final ActivityRecord activityInOtherTask = createActivityRecord(mDefaultDisplay); mDisplayContent.setFocusedApp(activityInOtherTask); mTransaction.requestFocusOnTaskFragment(token0); - mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + mWindowOrganizerController.applyTransaction(mTransaction); assertEquals(activityInOtherTask, mDisplayContent.mFocusedApp); @@ -566,7 +574,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { activity0.setState(ActivityRecord.State.PAUSED, "test"); activity1.setState(ActivityRecord.State.RESUMED, "test"); mDisplayContent.setFocusedApp(activity1); - mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + mWindowOrganizerController.applyTransaction(mTransaction); assertEquals(activity1, mDisplayContent.mFocusedApp); @@ -574,7 +582,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { // has a resumed activity. activity0.setState(ActivityRecord.State.RESUMED, "test"); mDisplayContent.setFocusedApp(activity1); - mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + mWindowOrganizerController.applyTransaction(mTransaction); assertEquals(activity0, mDisplayContent.mFocusedApp); } @@ -583,54 +591,51 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { public void testTaskFragmentInPip_startActivityInTaskFragment() { setupTaskFragmentInPip(); final ActivityRecord activity = mTaskFragment.getTopMostActivity(); - final IBinder errorToken = new Binder(); spyOn(mAtm.getActivityStartController()); - spyOn(mAtm.mWindowOrganizerController); + spyOn(mWindowOrganizerController); // Not allow to start activity in a TaskFragment that is in a PIP Task. mTransaction.startActivityInTaskFragment( mFragmentToken, activity.token, new Intent(), null /* activityOptions */) - .setErrorCallbackToken(errorToken); - mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + .setErrorCallbackToken(mErrorToken); + mWindowOrganizerController.applyTransaction(mTransaction); verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(), - any(), any(), anyInt(), anyInt()); + any(), any(), anyInt(), anyInt(), any()); verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), - eq(errorToken), any(IllegalArgumentException.class)); + eq(mErrorToken), any(IllegalArgumentException.class)); } @Test public void testTaskFragmentInPip_reparentActivityToTaskFragment() { setupTaskFragmentInPip(); final ActivityRecord activity = createActivityRecord(mDisplayContent); - final IBinder errorToken = new Binder(); - spyOn(mAtm.mWindowOrganizerController); + spyOn(mWindowOrganizerController); // Not allow to reparent activity to a TaskFragment that is in a PIP Task. mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token) - .setErrorCallbackToken(errorToken); - mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + .setErrorCallbackToken(mErrorToken); + mWindowOrganizerController.applyTransaction(mTransaction); - verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), - eq(errorToken), any(IllegalArgumentException.class)); + verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(mErrorToken), any(IllegalArgumentException.class)); assertNull(activity.getOrganizedTaskFragment()); } @Test public void testTaskFragmentInPip_setAdjacentTaskFragment() { setupTaskFragmentInPip(); - final IBinder errorToken = new Binder(); - spyOn(mAtm.mWindowOrganizerController); + spyOn(mWindowOrganizerController); // Not allow to set adjacent on a TaskFragment that is in a PIP Task. mTransaction.setAdjacentTaskFragments(mFragmentToken, null /* fragmentToken2 */, null /* options */) - .setErrorCallbackToken(errorToken); - mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + .setErrorCallbackToken(mErrorToken); + mWindowOrganizerController.applyTransaction(mTransaction); - verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), - eq(errorToken), any(IllegalArgumentException.class)); - verify(mTaskFragment, never()).setAdjacentTaskFragment(any(), anyBoolean()); + verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(mErrorToken), any(IllegalArgumentException.class)); + verify(mTaskFragment, never()).setAdjacentTaskFragment(any()); } @Test @@ -640,47 +645,45 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { ACTIVITY_TYPE_STANDARD); final ActivityRecord activity = createActivityRecord(pipTask); final IBinder fragmentToken = new Binder(); - final IBinder errorToken = new Binder(); - spyOn(mAtm.mWindowOrganizerController); + spyOn(mWindowOrganizerController); // Not allow to create TaskFragment in a PIP Task. createTaskFragmentFromOrganizer(mTransaction, activity, fragmentToken); - mTransaction.setErrorCallbackToken(errorToken); - mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + mTransaction.setErrorCallbackToken(mErrorToken); + mWindowOrganizerController.applyTransaction(mTransaction); - verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), - eq(errorToken), any(IllegalArgumentException.class)); - assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); + verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(mErrorToken), any(IllegalArgumentException.class)); + assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); } @Test public void testTaskFragmentInPip_deleteTaskFragment() { setupTaskFragmentInPip(); - final IBinder errorToken = new Binder(); - spyOn(mAtm.mWindowOrganizerController); + spyOn(mWindowOrganizerController); // Not allow to delete a TaskFragment that is in a PIP Task. mTransaction.deleteTaskFragment(mFragmentWindowToken) - .setErrorCallbackToken(errorToken); - mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + .setErrorCallbackToken(mErrorToken); + mWindowOrganizerController.applyTransaction(mTransaction); - verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), - eq(errorToken), any(IllegalArgumentException.class)); - assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(mFragmentToken)); + verify(mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(mErrorToken), any(IllegalArgumentException.class)); + assertNotNull(mWindowOrganizerController.getTaskFragment(mFragmentToken)); // Allow organizer to delete empty TaskFragment for cleanup. final Task task = mTaskFragment.getTask(); mTaskFragment.removeChild(mTaskFragment.getTopMostActivity()); - mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + mWindowOrganizerController.applyTransaction(mTransaction); - assertNull(mAtm.mWindowOrganizerController.getTaskFragment(mFragmentToken)); + assertNull(mWindowOrganizerController.getTaskFragment(mFragmentToken)); assertNull(task.getTopChild()); } @Test public void testTaskFragmentInPip_setConfig() { setupTaskFragmentInPip(); - spyOn(mAtm.mWindowOrganizerController); + spyOn(mWindowOrganizerController); // Set bounds is ignored on a TaskFragment that is in a PIP Task. mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); @@ -832,14 +835,13 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { final IBinder fragmentToken = new Binder(); createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken); mAtm.getWindowOrganizerController().applyTransaction(mTransaction); - final TaskFragment taskFragment = mAtm.mWindowOrganizerController - .getTaskFragment(fragmentToken); + final TaskFragment taskFragment = mWindowOrganizerController.getTaskFragment(fragmentToken); assertNotNull(taskFragment); taskFragment.removeImmediately(); - assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); + assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken)); } /** @@ -902,6 +904,101 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { assertApplyTransactionDisallowed(mTransaction); } + // TODO(b/232871351): add test for minimum dimension violation in startActivityInTaskFragment + @Test + public void testMinDimensionViolation_ReparentActivityToTaskFragment() { + final Task task = createTask(mDisplayContent); + final ActivityRecord activity = createActivityRecord(task); + // Make minWidth/minHeight exceeds the TaskFragment bounds. + activity.info.windowLayout = new ActivityInfo.WindowLayout( + 0, 0, 0, 0, 0, mTaskFragBounds.width() + 10, mTaskFragBounds.height() + 10); + mOrganizer.applyTransaction(mTransaction); + mController.registerOrganizer(mIOrganizer); + mTaskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(mFragmentToken) + .setOrganizer(mOrganizer) + .setBounds(mTaskFragBounds) + .build(); + doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity); + mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); + clearInvocations(mAtm.mRootWindowContainer); + + // Reparent activity to mTaskFragment, which is smaller than activity's + // minimum dimensions. + mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token) + .setErrorCallbackToken(mErrorToken); + mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), any(SecurityException.class)); + } + + @Test + public void testMinDimensionViolation_ReparentChildren() { + final Task task = createTask(mDisplayContent); + mOrganizer.applyTransaction(mTransaction); + mController.registerOrganizer(mIOrganizer); + final IBinder oldFragToken = new Binder(); + final TaskFragment oldTaskFrag = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(1) + .setFragmentToken(oldFragToken) + .setOrganizer(mOrganizer) + .build(); + final ActivityRecord activity = oldTaskFrag.getTopMostActivity(); + // Make minWidth/minHeight exceeds mTaskFragment bounds. + activity.info.windowLayout = new ActivityInfo.WindowLayout( + 0, 0, 0, 0, 0, mTaskFragBounds.width() + 10, mTaskFragBounds.height() + 10); + mTaskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(mFragmentToken) + .setOrganizer(mOrganizer) + .setBounds(mTaskFragBounds) + .build(); + doReturn(true).when(mTaskFragment).isAllowedToEmbedActivity(activity); + mWindowOrganizerController.mLaunchTaskFragments.put(oldFragToken, oldTaskFrag); + mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); + clearInvocations(mAtm.mRootWindowContainer); + + // Reparent oldTaskFrag's children to mTaskFragment, which is smaller than activity's + // minimum dimensions. + mTransaction.reparentChildren(oldTaskFrag.mRemoteToken.toWindowContainerToken(), + mTaskFragment.mRemoteToken.toWindowContainerToken()) + .setErrorCallbackToken(mErrorToken); + mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mOrganizer).onTaskFragmentError(eq(mErrorToken), any(SecurityException.class)); + } + + @Test + public void testMinDimensionViolation_SetBounds() { + final Task task = createTask(mDisplayContent); + mOrganizer.applyTransaction(mTransaction); + mController.registerOrganizer(mIOrganizer); + mTaskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(1) + .setFragmentToken(mFragmentToken) + .setOrganizer(mOrganizer) + .setBounds(new Rect(0, 0, mTaskFragBounds.right * 2, mTaskFragBounds.bottom * 2)) + .build(); + final ActivityRecord activity = mTaskFragment.getTopMostActivity(); + // Make minWidth/minHeight exceeds the TaskFragment bounds. + activity.info.windowLayout = new ActivityInfo.WindowLayout( + 0, 0, 0, 0, 0, mTaskFragBounds.width() + 10, mTaskFragBounds.height() + 10); + mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment); + clearInvocations(mAtm.mRootWindowContainer); + + // Shrink the TaskFragment to mTaskFragBounds to make its bounds smaller than activity's + // minimum dimensions. + mTransaction.setBounds(mTaskFragment.mRemoteToken.toWindowContainerToken(), mTaskFragBounds) + .setErrorCallbackToken(mErrorToken); + mWindowOrganizerController.applyTransaction(mTransaction); + + assertWithMessage("setBounds must not be performed.") + .that(mTaskFragment.getBounds()).isEqualTo(task.getBounds()); + } + /** * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls * {@link WindowOrganizerController#applyTransaction} to apply the transaction, diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 3ed484ac7391..228cb65aab38 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -202,7 +202,7 @@ public class TaskFragmentTest extends WindowTestsBase { doReturn(true).when(primaryActivity).supportsPictureInPicture(); doReturn(false).when(secondaryActivity).supportsPictureInPicture(); - primaryTf.setAdjacentTaskFragment(secondaryTf, false /* moveAdjacentTogether */); + primaryTf.setAdjacentTaskFragment(secondaryTf); primaryActivity.setState(RESUMED, "test"); secondaryActivity.setState(RESUMED, "test"); @@ -448,7 +448,7 @@ public class TaskFragmentTest extends WindowTestsBase { .setOrganizer(mOrganizer) .setFragmentToken(new Binder()) .build(); - tf0.setAdjacentTaskFragment(tf1, false /* moveAdjacentTogether */); + tf0.setAdjacentTaskFragment(tf1); tf0.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); tf1.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); task.setBounds(0, 0, 1200, 1000); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 202168bae869..46e21f1ffdbc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -31,7 +31,6 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; -import static android.util.DisplayMetrics.DENSITY_DEFAULT; import static android.view.IWindowManager.FIXED_TO_USER_ROTATION_ENABLED; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_90; @@ -60,7 +59,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.clearInvocations; @@ -497,34 +495,6 @@ public class TaskTests extends WindowTestsBase { assertEquals(reqBounds.height(), task.getBounds().height()); } - /** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */ - @Test - public void testBoundsOnModeChangeFreeformToFullscreen() { - DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay(); - Task rootTask = new TaskBuilder(mSupervisor).setDisplay(display).setCreateActivity(true) - .setWindowingMode(WINDOWING_MODE_FREEFORM).build(); - Task task = rootTask.getBottomMostTask(); - task.getRootActivity().setOrientation(SCREEN_ORIENTATION_UNSPECIFIED); - DisplayInfo info = new DisplayInfo(); - display.mDisplay.getDisplayInfo(info); - final Rect fullScreenBounds = new Rect(0, 0, info.logicalWidth, info.logicalHeight); - final Rect freeformBounds = new Rect(fullScreenBounds); - freeformBounds.inset((int) (freeformBounds.width() * 0.2), - (int) (freeformBounds.height() * 0.2)); - task.setBounds(freeformBounds); - - assertEquals(freeformBounds, task.getBounds()); - - // FULLSCREEN inherits bounds - rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); - assertEquals(fullScreenBounds, task.getBounds()); - assertEquals(freeformBounds, task.mLastNonFullscreenBounds); - - // FREEFORM restores bounds - rootTask.setWindowingMode(WINDOWING_MODE_FREEFORM); - assertEquals(freeformBounds, task.getBounds()); - } - /** * Tests that a task with forced orientation has orientation-consistent bounds within the * parent. @@ -590,6 +560,7 @@ public class TaskTests extends WindowTestsBase { // FULLSCREEN letterboxes bounds on activity level, no constraint on task level. rootTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + rootTask.setBounds(null); assertThat(task.getBounds().height()).isGreaterThan(task.getBounds().width()); assertThat(top.getBounds().width()).isGreaterThan(top.getBounds().height()); assertEquals(fullScreenBoundsPort, task.getBounds()); @@ -667,8 +638,6 @@ public class TaskTests extends WindowTestsBase { final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build(); final Configuration inOutConfig = new Configuration(); final Configuration parentConfig = new Configuration(); - final int longSide = 1200; - final int shortSide = 600; final Rect parentBounds = new Rect(0, 0, 250, 500); final Rect parentAppBounds = new Rect(0, 0, 250, 480); parentConfig.windowConfiguration.setBounds(parentBounds); @@ -689,14 +658,17 @@ public class TaskTests extends WindowTestsBase { // If bounds are overridden, config properties should be made to match. Surface hierarchy // will crop for policy. inOutConfig.setToDefaults(); + final int longSide = 960; + final int shortSide = 540; + parentConfig.densityDpi = 192; final Rect largerPortraitBounds = new Rect(0, 0, shortSide, longSide); inOutConfig.windowConfiguration.setBounds(largerPortraitBounds); task.computeConfigResourceOverrides(inOutConfig, parentConfig); // The override bounds are beyond the parent, the out appBounds should not be intersected // by parent appBounds. assertEquals(largerPortraitBounds, inOutConfig.windowConfiguration.getAppBounds()); - assertEquals(longSide, inOutConfig.screenHeightDp * parentConfig.densityDpi / 160); - assertEquals(shortSide, inOutConfig.screenWidthDp * parentConfig.densityDpi / 160); + assertEquals(800, inOutConfig.screenHeightDp); // 960/(192/160) = 800 + assertEquals(450, inOutConfig.screenWidthDp); // 540/(192/160) = 450 inOutConfig.setToDefaults(); // Landscape bounds. @@ -716,16 +688,17 @@ public class TaskTests extends WindowTestsBase { // Without limiting to be inside the parent bounds, the out screen size should keep relative // to the input bounds. final ActivityRecord activity = new ActivityBuilder(mAtm).setTask(task).build(); - final ActivityRecord.CompatDisplayInsets compatIntsets = + final ActivityRecord.CompatDisplayInsets compatInsets = new ActivityRecord.CompatDisplayInsets( display, activity, /* fixedOrientationBounds= */ null); - task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatIntsets); + task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatInsets); assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds()); - assertEquals((shortSide - statusBarHeight) * DENSITY_DEFAULT / parentConfig.densityDpi, - inOutConfig.screenHeightDp); - assertEquals(longSide * DENSITY_DEFAULT / parentConfig.densityDpi, - inOutConfig.screenWidthDp); + final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; + final int expectedHeightDp = (int) ((shortSide - statusBarHeight) / density + 0.5f); + assertEquals(expectedHeightDp, inOutConfig.screenHeightDp); + final int expectedWidthDp = (int) (longSide / density + 0.5f); + assertEquals(expectedWidthDp, inOutConfig.screenWidthDp); assertEquals(Configuration.ORIENTATION_LANDSCAPE, inOutConfig.orientation); } @@ -1443,25 +1416,6 @@ public class TaskTests extends WindowTestsBase { } @Test - public void testMoveToFront_moveAdjacentTask() { - final Task task1 = - createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); - final Task task2 = - createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); - spyOn(task2); - - task1.setAdjacentTaskFragment(task2, false /* moveTogether */); - task1.moveToFront("" /* reason */); - verify(task2, never()).moveToFrontInner(anyString(), isNull()); - - // Reset adjacent tasks to move together. - task1.setAdjacentTaskFragment(null, false /* moveTogether */); - task1.setAdjacentTaskFragment(task2, true /* moveTogether */); - task1.moveToFront("" /* reason */); - verify(task2).moveToFrontInner(anyString(), isNull()); - } - - @Test public void testResumeTask_doNotResumeTaskFragmentBehindTranslucent() { final Task task = createTask(mDisplayContent); final TaskFragment tfBehind = createTaskFragmentWithParentTask( diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index ea98b6b17e83..92457c73b315 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -26,7 +26,6 @@ import android.os.IBinder; import android.os.PowerManager.GoToSleepReason; import android.os.PowerManager.WakeReason; import android.util.proto.ProtoOutputStream; -import android.view.IWindowManager; import android.view.KeyEvent; import android.view.WindowManager; import android.view.animation.Animation; @@ -50,8 +49,7 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public void init(Context context, IWindowManager windowManager, - WindowManagerFuncs windowManagerFuncs) { + public void init(Context context, WindowManagerFuncs windowManagerFuncs) { } public void setDefaultDisplay(DisplayContentInfo displayContentInfo) { diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java index 6c1c0865446e..234bfa7ad772 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -85,9 +85,14 @@ import java.util.function.Function; @Presubmit @RunWith(WindowTestRunner.class) public class TransitionTests extends WindowTestsBase { + final SurfaceControl.Transaction mMockT = mock(SurfaceControl.Transaction.class); private Transition createTestTransition(int transitType) { - TransitionController controller = mock(TransitionController.class); + TransitionTracer tracer = mock(TransitionTracer.class); + final TransitionController controller = new TransitionController( + mock(ActivityTaskManagerService.class), mock(TaskSnapshotController.class), + mock(TransitionTracer.class)); + final BLASTSyncEngine sync = createTestBLASTSyncEngine(); final Transition t = new Transition(transitType, 0 /* flags */, controller, sync); t.startCollecting(0 /* timeoutMs */); @@ -121,7 +126,8 @@ public class TransitionTests extends WindowTestsBase { participants.add(oldTask); participants.add(newTask); ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); - TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, + mMockT); assertEquals(2, info.getChanges().size()); assertEquals(transit, info.getType()); @@ -129,7 +135,7 @@ public class TransitionTests extends WindowTestsBase { participants.add(opening); participants.add(closing); targets = Transition.calculateTargets(participants, changes); - info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); assertEquals(2, info.getChanges().size()); assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); @@ -137,7 +143,7 @@ public class TransitionTests extends WindowTestsBase { // Check combined prune and promote participants.remove(newTask); targets = Transition.calculateTargets(participants, changes); - info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); assertEquals(2, info.getChanges().size()); assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); @@ -145,7 +151,7 @@ public class TransitionTests extends WindowTestsBase { // Check multi promote participants.remove(oldTask); targets = Transition.calculateTargets(participants, changes); - info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); assertEquals(2, info.getChanges().size()); assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); @@ -186,7 +192,8 @@ public class TransitionTests extends WindowTestsBase { participants.add(opening); participants.add(opening2); ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); - TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, + mMockT); assertEquals(2, info.getChanges().size()); assertEquals(transit, info.getType()); assertNotNull(info.getChange(newTask.mRemoteToken.toWindowContainerToken())); @@ -195,7 +202,7 @@ public class TransitionTests extends WindowTestsBase { // Check that unchanging but visible descendant of sibling prevents promotion participants.remove(opening2); targets = Transition.calculateTargets(participants, changes); - info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); assertEquals(2, info.getChanges().size()); assertNotNull(info.getChange(newNestedTask.mRemoteToken.toWindowContainerToken())); assertNotNull(info.getChange(oldTask.mRemoteToken.toWindowContainerToken())); @@ -232,7 +239,8 @@ public class TransitionTests extends WindowTestsBase { participants.add(showing); participants.add(showing2); ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); - TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, + mMockT); assertEquals(1, info.getChanges().size()); assertEquals(transit, info.getType()); assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken())); @@ -240,14 +248,14 @@ public class TransitionTests extends WindowTestsBase { // Check that organized tasks get reported even if not top makeTaskOrganized(showTask); targets = Transition.calculateTargets(participants, changes); - info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); assertEquals(2, info.getChanges().size()); assertNotNull(info.getChange(tda.mRemoteToken.toWindowContainerToken())); assertNotNull(info.getChange(showTask.mRemoteToken.toWindowContainerToken())); // Even if DisplayArea explicitly participating participants.add(tda); targets = Transition.calculateTargets(participants, changes); - info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + info = Transition.calculateTransitionInfo(transit, flags, targets, changes, mMockT); assertEquals(2, info.getChanges().size()); } @@ -271,7 +279,7 @@ public class TransitionTests extends WindowTestsBase { ArrayList<WindowContainer> targets = Transition.calculateTargets( transition.mParticipants, transition.mChanges); TransitionInfo info = Transition.calculateTransitionInfo( - 0, 0, targets, transition.mChanges); + 0, 0, targets, transition.mChanges, mMockT); assertEquals(2, info.getChanges().size()); // There was an existence change on open, so it should be OPEN rather than SHOW assertEquals(TRANSIT_OPEN, @@ -308,7 +316,7 @@ public class TransitionTests extends WindowTestsBase { ArrayList<WindowContainer> targets = Transition.calculateTargets( transition.mParticipants, transition.mChanges); TransitionInfo info = Transition.calculateTransitionInfo( - 0, 0, targets, transition.mChanges); + 0, 0, targets, transition.mChanges, mMockT); assertEquals(taskCount, info.getChanges().size()); // verify order is top-to-bottem for (int i = 0; i < taskCount; ++i) { @@ -358,7 +366,7 @@ public class TransitionTests extends WindowTestsBase { ArrayList<WindowContainer> targets = Transition.calculateTargets( transition.mParticipants, transition.mChanges); TransitionInfo info = Transition.calculateTransitionInfo( - 0, 0, targets, transition.mChanges); + 0, 0, targets, transition.mChanges, mMockT); // verify that wallpaper is at bottom assertEquals(taskCount + 1, info.getChanges().size()); // The wallpaper is not organized, so it won't have a token; however, it will be marked @@ -392,7 +400,7 @@ public class TransitionTests extends WindowTestsBase { ArrayList<WindowContainer> targets = Transition.calculateTargets( transition.mParticipants, transition.mChanges); TransitionInfo info = Transition.calculateTransitionInfo( - 0, 0, targets, transition.mChanges); + 0, 0, targets, transition.mChanges, mMockT); // The wallpaper is not organized, so it won't have a token; however, it will be marked // as IS_WALLPAPER assertEquals(FLAG_IS_WALLPAPER, info.getChanges().get(0).getFlags()); @@ -474,7 +482,8 @@ public class TransitionTests extends WindowTestsBase { participants.add(changeTask); final ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); - TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, + mMockT); // Root changes should always be considered independent assertTrue(isIndependent( info.getChange(openTask.mRemoteToken.toWindowContainerToken()), info)); @@ -526,7 +535,8 @@ public class TransitionTests extends WindowTestsBase { participants.add(oldTask); participants.add(newTask); ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); - TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, + mMockT); assertEquals(2, info.getChanges().size()); assertEquals(transit, info.getType()); @@ -566,7 +576,8 @@ public class TransitionTests extends WindowTestsBase { participants.add(oldTask); participants.add(newTask); ArrayList<WindowContainer> targets = Transition.calculateTargets(participants, changes); - TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes); + TransitionInfo info = Transition.calculateTransitionInfo(transit, flags, targets, changes, + mMockT); assertEquals(2, info.getChanges().size()); assertEquals(transit, info.getType()); @@ -577,7 +588,7 @@ public class TransitionTests extends WindowTestsBase { @Test public void testTimeout() { final TransitionController controller = new TransitionController(mAtm, - mock(TaskSnapshotController.class)); + mock(TaskSnapshotController.class), mock(TransitionTracer.class)); final BLASTSyncEngine sync = new BLASTSyncEngine(mWm); final CountDownLatch latch = new CountDownLatch(1); // When the timeout is reached, it will finish the sync-group and notify transaction ready. @@ -608,7 +619,7 @@ public class TransitionTests extends WindowTestsBase { final int flags = 0; final TransitionInfo info = Transition.calculateTransitionInfo(transition.mType, flags, Transition.calculateTargets(transition.mParticipants, transition.mChanges), - transition.mChanges); + transition.mChanges, mMockT); transition.abort(); return info.getChanges().get(0); }; @@ -814,8 +825,11 @@ public class TransitionTests extends WindowTestsBase { player.onTransactionReady(mDisplayContent.getSyncTransaction()); final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation(); + final RemoteDisplayChangeController displayChangeController = mDisplayContent + .mRemoteDisplayChangeController; spyOn(displayRotation); - doReturn(true).when(displayRotation).isWaitingForRemoteRotation(); + spyOn(displayChangeController); + doReturn(true).when(displayChangeController).isWaitingForRemoteDisplayChange(); doReturn(prevRotation + 1).when(displayRotation).rotationForOrientation( anyInt() /* orientation */, anyInt() /* lastRotation */); // Rotation update is skipped while the recents animation is running. @@ -831,7 +845,8 @@ public class TransitionTests extends WindowTestsBase { @Test public void testIntermediateVisibility() { final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class); - final TransitionController controller = new TransitionController(mAtm, snapshotController); + final TransitionController controller = new TransitionController(mAtm, snapshotController, + mock(TransitionTracer.class)); final ITransitionPlayer player = new ITransitionPlayer.Default(); controller.registerTransitionPlayer(player, null /* appThread */); final Transition openTransition = controller.createTransition(TRANSIT_OPEN); @@ -895,7 +910,8 @@ public class TransitionTests extends WindowTestsBase { @Test public void testTransientLaunch() { final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class); - final TransitionController controller = new TransitionController(mAtm, snapshotController); + final TransitionController controller = new TransitionController(mAtm, snapshotController, + mock(TransitionTracer.class)); final ITransitionPlayer player = new ITransitionPlayer.Default(); controller.registerTransitionPlayer(player, null /* appThread */); final Transition openTransition = controller.createTransition(TRANSIT_OPEN); @@ -955,6 +971,30 @@ public class TransitionTests extends WindowTestsBase { verify(snapshotController, times(1)).recordTaskSnapshot(eq(task1), eq(false)); } + @Test + public void testNotReadyPushPop() { + final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class); + final TransitionController controller = new TransitionController(mAtm, snapshotController, + mock(TransitionTracer.class)); + final ITransitionPlayer player = new ITransitionPlayer.Default(); + controller.registerTransitionPlayer(player, null /* appThread */); + final Transition openTransition = controller.createTransition(TRANSIT_OPEN); + + // Start out with task2 visible and set up a transition that closes task2 and opens task1 + final Task task1 = createTask(mDisplayContent); + openTransition.collectExistenceChange(task1); + + assertFalse(openTransition.allReady()); + + openTransition.setAllReady(); + + openTransition.deferTransitionReady(); + assertFalse(openTransition.allReady()); + + openTransition.continueTransitionReady(); + assertTrue(openTransition.allReady()); + } + private static void makeTaskOrganized(Task... tasks) { final ITaskOrganizer organizer = mock(ITaskOrganizer.class); for (Task t : tasks) { diff --git a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java index 1f68608d2ce6..996382782b84 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WallpaperControllerTests.java @@ -35,6 +35,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; @@ -82,10 +83,7 @@ public class WallpaperControllerTests extends WindowTestsBase { assertFalse(dc.mWallpaperController.canScreenshotWallpaper()); // No wallpaper WSA Surface - WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), - true, dc, true /* ownerCanManageAppTokens */); - WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER, - wallpaperWindowToken, "wallpaperWindow"); + final WindowState wallpaperWindow = createWallpaperWindow(dc); assertFalse(dc.mWallpaperController.canScreenshotWallpaper()); // Wallpaper with not visible WSA surface. @@ -107,13 +105,10 @@ public class WallpaperControllerTests extends WindowTestsBase { @Test public void testWallpaperSizeWithFixedTransform() { // No wallpaper - final DisplayContent dc = createNewDisplay(); + final DisplayContent dc = mDisplayContent; // No wallpaper WSA Surface - WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), - true, dc, true /* ownerCanManageAppTokens */); - WindowState wallpaperWindow = createWindow(null /* parent */, TYPE_WALLPAPER, - wallpaperWindowToken, "wallpaperWindow"); + final WindowState wallpaperWindow = createWallpaperWindow(dc); WindowManager.LayoutParams attrs = wallpaperWindow.getAttrs(); Rect bounds = dc.getBounds(); @@ -153,7 +148,7 @@ public class WallpaperControllerTests extends WindowTestsBase { final WmDisplayCutout cutout = dc.calculateDisplayCutoutForRotation(Surface.ROTATION_0); final DisplayFrames displayFrames = new DisplayFrames(dc.getDisplayId(), new InsetsState(), info, cutout, RoundedCorners.NO_ROUNDED_CORNERS, new PrivacyIndicatorBounds()); - wallpaperWindowToken.applyFixedRotationTransform(info, displayFrames, config); + wallpaperWindow.mToken.applyFixedRotationTransform(info, displayFrames, config); // Check that the wallpaper has the same frame in landscape than in portrait assertEquals(Configuration.ORIENTATION_LANDSCAPE, dc.getConfiguration().orientation); @@ -163,10 +158,7 @@ public class WallpaperControllerTests extends WindowTestsBase { @Test public void testWallpaperZoom() throws RemoteException { final DisplayContent dc = mWm.mRoot.getDefaultDisplay(); - final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, - mock(IBinder.class), true, dc, true /* ownerCanManageAppTokens */); - final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, - "wallpaperWindow"); + final WindowState wallpaperWindow = createWallpaperWindow(dc); wallpaperWindow.getAttrs().privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; @@ -189,10 +181,7 @@ public class WallpaperControllerTests extends WindowTestsBase { @Test public void testWallpaperZoom_shouldNotScaleWallpaper() throws RemoteException { final DisplayContent dc = mWm.mRoot.getDefaultDisplay(); - final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, - mock(IBinder.class), true, dc, true /* ownerCanManageAppTokens */); - final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, - "wallpaperWindow"); + final WindowState wallpaperWindow = createWallpaperWindow(dc); wallpaperWindow.getAttrs().privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; @@ -219,11 +208,7 @@ public class WallpaperControllerTests extends WindowTestsBase { @Test public void testWallpaperZoom_multipleCallers() { final DisplayContent dc = mWm.mRoot.getDefaultDisplay(); - final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, - mock(IBinder.class), true, dc, - true /* ownerCanManageAppTokens */); - final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, - "wallpaperWindow"); + final WindowState wallpaperWindow = createWallpaperWindow(dc); wallpaperWindow.getAttrs().privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS; @@ -278,37 +263,51 @@ public class WallpaperControllerTests extends WindowTestsBase { assertEquals(WINDOWING_MODE_FULLSCREEN, token.getWindowingMode()); } - @UseTestDisplay(addWindows = W_ACTIVITY) @Test public void testFixedRotationRecentsAnimatingTask() { + final WindowState wallpaperWindow = createWallpaperWindow(mDisplayContent); + final WallpaperWindowToken wallpaperToken = wallpaperWindow.mToken.asWallpaperToken(); + final WindowState appWin = createWindow(null, TYPE_BASE_APPLICATION, "app"); + makeWindowVisible(appWin); + final ActivityRecord r = appWin.mActivityRecord; final RecentsAnimationController recentsController = mock(RecentsAnimationController.class); - doReturn(true).when(recentsController).isWallpaperVisible(eq(mAppWindow)); + doReturn(true).when(recentsController).isWallpaperVisible(eq(appWin)); mWm.setRecentsAnimationController(recentsController); - mAppWindow.mActivityRecord.applyFixedRotationTransform(mDisplayContent.getDisplayInfo(), + r.applyFixedRotationTransform(mDisplayContent.getDisplayInfo(), mDisplayContent.mDisplayFrames, mDisplayContent.getConfiguration()); - mAppWindow.mActivityRecord.mVisibleRequested = true; + // Invisible requested activity should not share its rotation transform. + r.mVisibleRequested = false; mDisplayContent.mWallpaperController.adjustWallpaperWindows(); + assertFalse(wallpaperToken.hasFixedRotationTransform()); - assertEquals(mAppWindow, mDisplayContent.mWallpaperController.getWallpaperTarget()); // Wallpaper should link the transform of its target. - assertTrue(mAppWindow.mActivityRecord.hasFixedRotationTransform()); - - mAppWindow.mActivityRecord.finishFixedRotationTransform(); - // Invisible requested activity should not share its rotation transform. - mAppWindow.mActivityRecord.mVisibleRequested = false; + r.mVisibleRequested = true; mDisplayContent.mWallpaperController.adjustWallpaperWindows(); + assertEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget()); + assertTrue(r.hasFixedRotationTransform()); + assertTrue(wallpaperToken.hasFixedRotationTransform()); - assertFalse(mAppWindow.mActivityRecord.hasFixedRotationTransform()); + // The case with shell transition. + registerTestTransitionPlayer(); + final Transition t = r.mTransitionController.createTransition(TRANSIT_OPEN); + final ActivityRecord recents = mock(ActivityRecord.class); + t.collect(r.getTask()); + r.mTransitionController.setTransientLaunch(recents, r.getTask()); + // The activity in restore-below task should not be the target if keyguard is not locked. + mDisplayContent.mWallpaperController.adjustWallpaperWindows(); + assertNotEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget()); + // The activity in restore-below task should be the target if keyguard is occluded. + doReturn(true).when(mDisplayContent).isKeyguardLocked(); + mDisplayContent.mWallpaperController.adjustWallpaperWindows(); + assertEquals(appWin, mDisplayContent.mWallpaperController.getWallpaperTarget()); } @Test public void testWallpaperTokenVisibility() { final DisplayContent dc = mWm.mRoot.getDefaultDisplay(); - final WallpaperWindowToken token = new WallpaperWindowToken(mWm, mock(IBinder.class), - true, dc, true /* ownerCanManageAppTokens */); - final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, token, - "wallpaperWindow"); + final WindowState wallpaperWindow = createWallpaperWindow(dc); + final WallpaperWindowToken token = wallpaperWindow.mToken.asWallpaperToken(); wallpaperWindow.setHasSurface(true); // Set-up mock shell transitions @@ -350,6 +349,13 @@ public class WallpaperControllerTests extends WindowTestsBase { assertTrue(token.isVisible()); } + private WindowState createWallpaperWindow(DisplayContent dc) { + final WindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, mock(IBinder.class), + true /* explicit */, dc, true /* ownerCanManageAppTokens */); + return createWindow(null /* parent */, TYPE_WALLPAPER, wallpaperWindowToken, + "wallpaperWindow"); + } + private WindowState createWallpaperTargetWindow(DisplayContent dc) { final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService) .setTask(dc.getDefaultTaskDisplayArea().getRootHomeTask()) diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 40ca2506fab1..08bad70a1411 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -522,7 +522,10 @@ public class WindowOrganizerTests extends WindowTestsBase { mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); assertEquals(WINDOWING_MODE_FULLSCREEN, record.getWindowingMode()); - assertEquals(WINDOWING_MODE_PINNED, rootTask.getWindowingMode()); + // Get the root task from the PIP activity record again, since the PIP root task may have + // changed when the activity entered PIP mode. + final Task pipRootTask = record.getRootTask(); + assertEquals(WINDOWING_MODE_PINNED, pipRootTask.getWindowingMode()); } @Test @@ -687,7 +690,7 @@ public class WindowOrganizerTests extends WindowTestsBase { final RunningTaskInfo info2 = task2.getTaskInfo(); WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.setAdjacentRoots(info1.token, info2.token, false /* moveTogether */); + wct.setAdjacentRoots(info1.token, info2.token); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertEquals(task1.getAdjacentTaskFragment(), task2); assertEquals(task2.getAdjacentTaskFragment(), task1); @@ -697,8 +700,8 @@ public class WindowOrganizerTests extends WindowTestsBase { mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); assertEquals(dc.getDefaultTaskDisplayArea().mLaunchAdjacentFlagRootTask, task1); - task1.setAdjacentTaskFragment(null, false /* moveTogether */); - task2.setAdjacentTaskFragment(null, false /* moveTogether */); + task1.setAdjacentTaskFragment(null); + task2.setAdjacentTaskFragment(null); wct = new WindowContainerTransaction(); wct.clearLaunchAdjacentFlagRoot(info1.token); mWm.mAtmService.mWindowOrganizerController.applyTransaction(wct); @@ -1015,6 +1018,8 @@ public class WindowOrganizerTests extends WindowTestsBase { final ActivityRecord record = createActivityRecordWithParentTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); record.info.flags |= ActivityInfo.FLAG_SUPPORTS_PICTURE_IN_PICTURE; + record.setPictureInPictureParams(new PictureInPictureParams.Builder() + .setAutoEnterEnabled(true).build()); spyOn(record); doReturn(true).when(record).checkEnterPictureInPictureState(any(), anyBoolean()); @@ -1499,7 +1504,11 @@ public class WindowOrganizerTests extends WindowTestsBase { verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); clearInvocations(mWm.mAtmService.mRootWindowContainer); - t.setWindowingMode(wct, WINDOWING_MODE_FULLSCREEN); + // The token for the PIP root task may have changed when the task entered PIP mode, so do + // not reuse the one from above. + final WindowContainerToken newToken = + record.getRootTask().mRemoteToken.toWindowContainerToken(); + t.setWindowingMode(newToken, WINDOWING_MODE_FULLSCREEN); mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); verify(mWm.mAtmService.mRootWindowContainer).resumeFocusedTasksTopActivities(); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java index 746f2b50ac3a..3abf7ce665ae 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java @@ -21,6 +21,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; @@ -39,24 +40,30 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.Manifest; +import android.app.ActivityManager; +import android.app.ClientTransactionHandler; import android.app.IApplicationThread; +import android.app.servertransaction.ConfigurationChangeItem; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.LocaleList; +import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.InOrder; import org.mockito.Mockito; @@ -282,6 +289,39 @@ public class WindowProcessControllerTests extends WindowTestsBase { } @Test + public void testCachedStateConfigurationChange() throws RemoteException { + final ClientLifecycleManager clientManager = mAtm.getLifecycleManager(); + doNothing().when(clientManager).scheduleTransaction(any(), any()); + final IApplicationThread thread = mWpc.getThread(); + final Configuration newConfig = new Configuration(mWpc.getConfiguration()); + newConfig.densityDpi += 100; + // Non-cached state will send the change directly. + mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND); + clearInvocations(clientManager); + mWpc.onConfigurationChanged(newConfig); + verify(clientManager).scheduleTransaction(eq(thread), any()); + + // Cached state won't send the change. + clearInvocations(clientManager); + mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY); + newConfig.densityDpi += 100; + mWpc.onConfigurationChanged(newConfig); + verify(clientManager, never()).scheduleTransaction(eq(thread), any()); + + // Cached -> non-cached will send the previous deferred config immediately. + mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER); + final ArgumentCaptor<ConfigurationChangeItem> captor = + ArgumentCaptor.forClass(ConfigurationChangeItem.class); + verify(clientManager).scheduleTransaction(eq(thread), captor.capture()); + final ClientTransactionHandler client = mock(ClientTransactionHandler.class); + captor.getValue().preExecute(client, null /* token */); + final ArgumentCaptor<Configuration> configCaptor = + ArgumentCaptor.forClass(Configuration.class); + verify(client).updatePendingConfiguration(configCaptor.capture()); + assertEquals(newConfig, configCaptor.getValue()); + } + + @Test public void testComputeOomAdjFromActivities() { final ActivityRecord activity = createActivityRecord(mWpc); activity.mVisibleRequested = true; diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index 724204f79eab..378aff81ec3c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -673,6 +673,14 @@ public class WindowStateTests extends WindowTestsBase { // Keyguard host window should be always contained. The drawn app or app with starting // window are unnecessary to draw. assertEquals(Arrays.asList(keyguardHostWindow, startingWindow), outWaitingForDrawn); + + // No need to wait for a window of invisible activity even if the window has surface. + final WindowState invisibleApp = mAppWindow; + invisibleApp.mActivityRecord.mVisibleRequested = false; + invisibleApp.mActivityRecord.allDrawn = false; + outWaitingForDrawn.clear(); + invisibleApp.requestDrawIfNeeded(outWaitingForDrawn); + assertTrue(outWaitingForDrawn.isEmpty()); } @UseTestDisplay(addWindows = W_ABOVE_ACTIVITY) diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 9957d05d0a52..50fa4cc9eb7c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -229,14 +229,28 @@ class WindowTestsBase extends SystemServiceTestsBase { // {@link com.android.internal.R.dimen.config_fixedOrientationLetterboxAspectRatio}, is set // on some device form factors. mAtm.mWindowManager.mLetterboxConfiguration.setFixedOrientationLetterboxAspectRatio(0); - // Ensure letterbox position multiplier is not overridden on any device target. + // Ensure letterbox horizontal position multiplier is not overridden on any device target. // {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}, // may be set on some device form factors. mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxHorizontalPositionMultiplier(0.5f); - // Ensure letterbox reachability treatment isn't overridden on any device target. - // {@link com.android.internal.R.bool.config_letterboxIsReachabilityEnabled}, + // Ensure letterbox vertical position multiplier is not overridden on any device target. + // {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier}, + // may be set on some device form factors. + mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f); + // Ensure letterbox horizontal reachability treatment isn't overridden on any device target. + // {@link com.android.internal.R.bool.config_letterboxIsHorizontalReachabilityEnabled}, // may be set on some device form factors. - mAtm.mWindowManager.mLetterboxConfiguration.setIsReachabilityEnabled(false); + mAtm.mWindowManager.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(false); + // Ensure letterbox vertical reachability treatment isn't overridden on any device target. + // {@link com.android.internal.R.bool.config_letterboxIsVerticalReachabilityEnabled}, + // may be set on some device form factors. + mAtm.mWindowManager.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(false); + // Ensure aspect ratio for unresizable apps isn't overridden on any device target. + // {@link com.android.internal.R.bool + // .config_letterboxIsSplitScreenAspectRatioForUnresizableAppsEnabled}, may be set on some + // device form factors. + mAtm.mWindowManager.mLetterboxConfiguration + .setIsSplitScreenAspectRatioForUnresizableAppsEnabled(false); checkDeviceSpecificOverridesNotApplied(); } @@ -246,7 +260,11 @@ class WindowTestsBase extends SystemServiceTestsBase { // Revert back to device overrides. mAtm.mWindowManager.mLetterboxConfiguration.resetFixedOrientationLetterboxAspectRatio(); mAtm.mWindowManager.mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); - mAtm.mWindowManager.mLetterboxConfiguration.resetIsReachabilityEnabled(); + mAtm.mWindowManager.mLetterboxConfiguration.resetLetterboxVerticalPositionMultiplier(); + mAtm.mWindowManager.mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled(); + mAtm.mWindowManager.mLetterboxConfiguration.resetIsVerticalReachabilityEnabled(); + mAtm.mWindowManager.mLetterboxConfiguration + .resetIsSplitScreenAspectRatioForUnresizableAppsEnabled(); } /** @@ -1199,6 +1217,7 @@ class WindowTestsBase extends SystemServiceTestsBase { @Nullable private TaskFragmentOrganizer mOrganizer; private IBinder mFragmentToken; + private Rect mBounds; TaskFragmentBuilder(ActivityTaskManagerService service) { mAtm = service; @@ -1235,6 +1254,11 @@ class WindowTestsBase extends SystemServiceTestsBase { return this; } + TaskFragmentBuilder setBounds(@Nullable Rect bounds) { + mBounds = bounds; + return this; + } + TaskFragment build() { SystemServicesTestRule.checkHoldsLock(mAtm.mGlobalLock); @@ -1262,6 +1286,9 @@ class WindowTestsBase extends SystemServiceTestsBase { mOrganizer.getOrganizerToken(), DEFAULT_TASK_FRAGMENT_ORGANIZER_UID, DEFAULT_TASK_FRAGMENT_ORGANIZER_PROCESS_NAME); } + if (mBounds != null && !mBounds.isEmpty()) { + taskFragment.setBounds(mBounds); + } spyOn(taskFragment); return taskFragment; } @@ -1541,7 +1568,7 @@ class WindowTestsBase extends SystemServiceTestsBase { mSecondary = mService.mTaskOrganizerController.createRootTask( display, WINDOWING_MODE_MULTI_WINDOW, null); - mPrimary.setAdjacentTaskFragment(mSecondary, true); + mPrimary.setAdjacentTaskFragment(mSecondary); display.getDefaultTaskDisplayArea().setLaunchAdjacentFlagRootTask(mSecondary); final Rect primaryBounds = new Rect(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java index 2df1d23c0497..77fca451547d 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java @@ -328,7 +328,6 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mImeWindow, imeSystemOverlayTarget); assertWindowHigher(mImeWindow, mChildAppWindowAbove); assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mImeWindow, mDockedDividerWindow); // The IME has a higher base layer than the status bar so we may expect it to go // above the status bar once they are both in the Non-App layer, as past versions of this @@ -349,7 +348,6 @@ public class ZOrderingTests extends WindowTestsBase { assertWindowHigher(mImeWindow, mChildAppWindowAbove); assertWindowHigher(mImeWindow, mAppWindow); - assertWindowHigher(mImeWindow, mDockedDividerWindow); assertWindowHigher(mImeWindow, mStatusBarWindow); // And, IME dialogs should always have an higher layer than the IME. @@ -489,77 +487,6 @@ public class ZOrderingTests extends WindowTestsBase { } @Test - public void testDockedDividerPosition() { - final Task pinnedTask = - createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); - final WindowState pinnedWindow = - createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow"); - - final Task belowTask = - createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - final WindowState belowTaskWindow = - createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow"); - - final Task splitScreenTask1 = - createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); - final WindowState splitWindow1 = - createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1"); - final Task splitScreenTask2 = - createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); - final WindowState splitWindow2 = - createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2"); - splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */); - splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */); - - final Task aboveTask = - createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - final WindowState aboveTaskWindow = - createAppWindow(aboveTask, ACTIVITY_TYPE_STANDARD, "aboveTaskWindow"); - - mDisplayContent.assignChildLayers(mTransaction); - - assertWindowHigher(splitWindow1, belowTaskWindow); - assertWindowHigher(splitWindow2, belowTaskWindow); - assertWindowHigher(mDockedDividerWindow, splitWindow1); - assertWindowHigher(mDockedDividerWindow, splitWindow2); - assertWindowHigher(aboveTaskWindow, mDockedDividerWindow); - assertWindowHigher(pinnedWindow, aboveTaskWindow); - } - - - @Test - public void testDockedDividerPosition_noAboveTask() { - final Task pinnedTask = - createTask(mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); - final WindowState pinnedWindow = - createAppWindow(pinnedTask, ACTIVITY_TYPE_STANDARD, "pinnedWindow"); - - final Task belowTask = - createTask(mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); - final WindowState belowTaskWindow = - createAppWindow(belowTask, ACTIVITY_TYPE_STANDARD, "belowTaskWindow"); - - final Task splitScreenTask1 = - createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); - final WindowState splitWindow1 = - createAppWindow(splitScreenTask1, ACTIVITY_TYPE_STANDARD, "splitWindow1"); - final Task splitScreenTask2 = - createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD); - final WindowState splitWindow2 = - createAppWindow(splitScreenTask2, ACTIVITY_TYPE_STANDARD, "splitWindow2"); - splitScreenTask1.setAdjacentTaskFragment(splitScreenTask2, true /* moveTogether */); - splitScreenTask2.setAdjacentTaskFragment(splitScreenTask1, true /* moveTogether */); - - mDisplayContent.assignChildLayers(mTransaction); - - assertWindowHigher(splitWindow1, belowTaskWindow); - assertWindowHigher(splitWindow2, belowTaskWindow); - assertWindowHigher(mDockedDividerWindow, splitWindow1); - assertWindowHigher(mDockedDividerWindow, splitWindow2); - assertWindowHigher(pinnedWindow, mDockedDividerWindow); - } - - @Test public void testAttachNavBarWhenEnteringRecents_expectNavBarHigherThanIme() { // create RecentsAnimationController IRecentsAnimationRunner mockRunner = mock(IRecentsAnimationRunner.class); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 42d446d058b4..434663bd8167 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -46,11 +46,9 @@ import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPH import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK; -import static com.android.server.voiceinteraction.SoundTriggerSessionPermissionsDecorator.enforcePermissionForPreflight; import android.annotation.NonNull; import android.annotation.Nullable; -import android.app.AppOpsManager; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.Context; @@ -1162,11 +1160,12 @@ final class HotwordDetectionConnection { // TODO: Share this code with SoundTriggerMiddlewarePermission. private void enforcePermissionsForDataDelivery() { Binder.withCleanCallingIdentity(() -> { - enforcePermissionForPreflight(mContext, mVoiceInteractorIdentity, RECORD_AUDIO); - int hotwordOp = AppOpsManager.strOpToOp(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD); - mContext.getSystemService(AppOpsManager.class).noteOpNoThrow(hotwordOp, - mVoiceInteractorIdentity.uid, mVoiceInteractorIdentity.packageName, - mVoiceInteractorIdentity.attributionTag, OP_MESSAGE); + // Hack to make sure we show the mic privacy-indicator since the Trusted Hotword + // requirement isn't being enforced for now. Normally, we would note the HOTWORD op here + // instead. + enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity, + RECORD_AUDIO, OP_MESSAGE); + enforcePermissionForDataDelivery(mContext, mVoiceInteractorIdentity, CAPTURE_AUDIO_HOTWORD, OP_MESSAGE); }); diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt index 78aea1f1fb17..933a3a0458ee 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowFromFixedOrientationAppTest.kt @@ -23,16 +23,16 @@ import android.view.Surface import android.view.WindowManagerPolicyConstants import androidx.test.filters.FlakyTest import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.tapl.LauncherInstrumentation import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group2 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.navBarLayerPositionEnd import com.android.server.wm.flicker.navBarWindowIsVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsVisible @@ -43,7 +43,8 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** - * Test IME window layer will become visible when switching from the fixed orientation activity. + * Test IME window layer will become visible when switching from the fixed orientation activity + * (e.g. Launcher activity). * To run this test: `atest FlickerTests:OpenImeWindowFromFixedOrientationAppTest` */ @RequiresDevice @@ -53,24 +54,31 @@ import org.junit.runners.Parameterized @Group2 class OpenImeWindowFromFixedOrientationAppTest(private val testSpec: FlickerTestParameter) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - private val fixedOrientationApp = FixedOrientationAppHelper(instrumentation) private val imeTestApp = ImeAppAutoFocusHelper(instrumentation, testSpec.startRotation) + private val taplInstrumentation = LauncherInstrumentation() @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { return FlickerBuilder(instrumentation).apply { setup { + test { + // Launch the activity with expecting IME will be shown. + imeTestApp.launchViaIntent(wmHelper) + } eachRun { - fixedOrientationApp.launchViaIntent(wmHelper) - this.setRotation(Surface.ROTATION_90) + // Swiping out the IME activity to home. + taplInstrumentation.goHome() + wmHelper.waitForHomeActivityVisible() } } transitions { + // Bring the exist IME activity to the front in landscape mode device rotation. + setRotation(Surface.ROTATION_90) imeTestApp.launchViaIntent(wmHelper) } teardown { test { - fixedOrientationApp.exit(wmHelper) + imeTestApp.exit(wmHelper) } } } @@ -90,7 +98,7 @@ class OpenImeWindowFromFixedOrientationAppTest(private val testSpec: FlickerTest @Presubmit @Test - fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales() + fun navBarLayerRotatesAndScales() = testSpec.navBarLayerPositionEnd() @FlakyTest(bugId = 206753786) fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales() @@ -112,7 +120,7 @@ class OpenImeWindowFromFixedOrientationAppTest(private val testSpec: FlickerTest return FlickerTestParameterFactory.getInstance() .getConfigNonRotationTests( repetitions = 3, - supportedRotations = listOf(Surface.ROTATION_0), + supportedRotations = listOf(Surface.ROTATION_90), supportedNavigationModes = listOf( WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY ) diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 43aa4b151548..3e2130dc480f 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -45,6 +45,7 @@ android:theme="@style/CutoutShortEdges" android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus" android:windowSoftInputMode="stateVisible" + android:configChanges="orientation|screenSize" android:label="ImeAppAutoFocus" android:exported="true"> <intent-filter> diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java index 1fe13fe97fbe..06cbeb5368a5 100644 --- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java @@ -124,16 +124,6 @@ public class WindowManagerPermissionTests extends TestCase { @SmallTest public void testSET_ORIENTATION() { try { - mWm.updateRotation(true, false); - fail("IWindowManager.updateRotation did not throw SecurityException as" - + " expected"); - } catch (SecurityException e) { - // expected - } catch (RemoteException e) { - fail("Unexpected remote exception"); - } - - try { mWm.freezeRotation(-1); fail("IWindowManager.freezeRotation did not throw SecurityException as" + " expected"); |