diff options
843 files changed, 16827 insertions, 9180 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 0de0a1cf9c8e..e8bcfd2f5bb8 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -112,6 +112,7 @@ import android.text.TextUtils; import android.text.format.DateFormat; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.EventLog; import android.util.IndentingPrintWriter; import android.util.Log; import android.util.LongArrayQueue; @@ -334,12 +335,18 @@ public class AlarmManagerService extends SystemService { "REORDER_ALARMS_FOR_TARE", }); - BroadcastOptions mOptsWithFgs = BroadcastOptions.makeBasic(); - BroadcastOptions mOptsWithFgsForAlarmClock = BroadcastOptions.makeBasic(); - BroadcastOptions mOptsWithoutFgs = BroadcastOptions.makeBasic(); - BroadcastOptions mOptsTimeBroadcast = BroadcastOptions.makeBasic(); + BroadcastOptions mOptsWithFgs = makeBasicAlarmBroadcastOptions(); + BroadcastOptions mOptsWithFgsForAlarmClock = makeBasicAlarmBroadcastOptions(); + BroadcastOptions mOptsWithoutFgs = makeBasicAlarmBroadcastOptions(); + BroadcastOptions mOptsTimeBroadcast = makeBasicAlarmBroadcastOptions(); ActivityOptions mActivityOptsRestrictBal = ActivityOptions.makeBasic(); - BroadcastOptions mBroadcastOptsRestrictBal = BroadcastOptions.makeBasic(); + BroadcastOptions mBroadcastOptsRestrictBal = makeBasicAlarmBroadcastOptions(); + + private static BroadcastOptions makeBasicAlarmBroadcastOptions() { + final BroadcastOptions b = BroadcastOptions.makeBasic(); + b.setAlarmBroadcast(true); + return b; + } // TODO(b/172085676): Move inside alarm store. private final SparseArray<AlarmManager.AlarmClockInfo> mNextAlarmClockForUser = @@ -2299,7 +2306,11 @@ public class AlarmManagerService extends SystemService { + " reached for uid: " + UserHandle.formatUid(callingUid) + ", callingPackage: " + callingPackage; Slog.w(TAG, errorMsg); - throw new IllegalStateException(errorMsg); + if (callingUid != Process.SYSTEM_UID) { + throw new IllegalStateException(errorMsg); + } else { + EventLog.writeEvent(0x534e4554, "234441463", -1, errorMsg); + } } setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, interval, operation, directReceiver, listenerTag, flags, workSource, alarmClock, callingUid, diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ec4ad8b704c3..0126199add0c 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -5956,7 +5956,7 @@ package android.location { method public int getEphemerisSource(); method @FloatRange public double getIonoDelayMeters(); method @IntRange(from=0, to=1023) public int getIssueOfDataClock(); - method @IntRange(from=0, to=255) public int getIssueOfDataEphemeris(); + method @IntRange(from=0, to=1023) public int getIssueOfDataEphemeris(); method @Nullable public android.location.SatellitePvt.PositionEcef getPositionEcef(); method @IntRange(from=0) public long getTimeOfClockSeconds(); method @IntRange(from=0) public long getTimeOfEphemerisSeconds(); @@ -5984,7 +5984,7 @@ package android.location { method @NonNull public android.location.SatellitePvt.Builder setEphemerisSource(int); method @NonNull public android.location.SatellitePvt.Builder setIonoDelayMeters(@FloatRange(from=0.0f, to=100.0f) double); method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataClock(@IntRange(from=0, to=1023) int); - method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataEphemeris(@IntRange(from=0, to=255) int); + method @NonNull public android.location.SatellitePvt.Builder setIssueOfDataEphemeris(@IntRange(from=0, to=1023) int); method @NonNull public android.location.SatellitePvt.Builder setPositionEcef(@NonNull android.location.SatellitePvt.PositionEcef); method @NonNull public android.location.SatellitePvt.Builder setTimeOfClockSeconds(@IntRange(from=0) long); method @NonNull public android.location.SatellitePvt.Builder setTimeOfEphemerisSeconds(@IntRange(from=0) long); diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java index 1d3b5e2aa4ee..482f456b5d83 100644 --- a/core/java/android/app/ActivityClient.java +++ b/core/java/android/app/ActivityClient.java @@ -227,6 +227,18 @@ public class ActivityClient { } /** + * Returns the windowing mode of the task that hosts the activity, or {@code -1} if task is not + * found. + */ + public int getTaskWindowingMode(IBinder activityToken) { + try { + return getActivityClientController().getTaskWindowingMode(activityToken); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns the non-finishing activity token below in the same task if it belongs to the same * process. */ diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index d6441a2b629b..4fc3254ed1a3 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -1149,7 +1149,6 @@ public class ActivityOptions extends ComponentOptions { opts.mLaunchIntoPipParams = new PictureInPictureParams.Builder(pictureInPictureParams) .setIsLaunchIntoPip(true) .build(); - opts.mLaunchBounds = new Rect(pictureInPictureParams.getSourceRectHint()); return opts; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d19ecd450f0a..f384fa9e6a0b 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5863,20 +5863,20 @@ public final class ActivityThread extends ClientTransactionHandler final boolean movedToDifferentDisplay = isDifferentDisplay(activity.getDisplayId(), displayId); - final Configuration currentConfig = activity.mCurrentConfig; - final int diff = currentConfig.diffPublicOnly(newConfig); - final boolean hasPublicConfigChange = diff != 0; + final Configuration currentResConfig = activity.getResources().getConfiguration(); + final int diff = currentResConfig.diffPublicOnly(newConfig); + final boolean hasPublicResConfigChange = diff != 0; final ActivityClientRecord r = getActivityClient(activityToken); // TODO(b/173090263): Use diff instead after the improvement of AssetManager and // ResourcesImpl constructions. - final boolean shouldUpdateResources = hasPublicConfigChange - || shouldUpdateResources(activityToken, currentConfig, newConfig, amOverrideConfig, - movedToDifferentDisplay, hasPublicConfigChange); - final boolean shouldReportChange = shouldReportChange(diff, currentConfig, newConfig, + final boolean shouldUpdateResources = hasPublicResConfigChange + || shouldUpdateResources(activityToken, currentResConfig, newConfig, + amOverrideConfig, movedToDifferentDisplay, hasPublicResConfigChange); + final boolean shouldReportChange = shouldReportChange(activity.mCurrentConfig, newConfig, r != null ? r.mSizeConfigurations : null, activity.mActivityInfo.getRealConfigChanged()); // Nothing significant, don't proceed with updating and reporting. - if (!shouldUpdateResources) { + if (!shouldUpdateResources && !shouldReportChange) { return null; } @@ -5896,9 +5896,6 @@ public final class ActivityThread extends ClientTransactionHandler amOverrideConfig, contextThemeWrapperOverrideConfig); mResourcesManager.updateResourcesForActivity(activityToken, finalOverrideConfig, displayId); - activity.mConfigChangeFlags = 0; - activity.mCurrentConfig = new Configuration(newConfig); - // Apply the ContextThemeWrapper override if necessary. // NOTE: Make sure the configurations are not modified, as they are treated as immutable // in many places. @@ -5909,8 +5906,10 @@ public final class ActivityThread extends ClientTransactionHandler activity.dispatchMovedToDisplay(displayId, configToReport); } + activity.mConfigChangeFlags = 0; if (shouldReportChange) { activity.mCalled = false; + activity.mCurrentConfig = new Configuration(newConfig); activity.onConfigurationChanged(configToReport); if (!activity.mCalled) { throw new SuperNotCalledException("Activity " + activity.getLocalClassName() + @@ -5925,8 +5924,6 @@ public final class ActivityThread extends ClientTransactionHandler * Returns {@code true} if {@link Activity#onConfigurationChanged(Configuration)} should be * dispatched. * - * @param publicDiff Usually computed by {@link Configuration#diffPublicOnly(Configuration)}. - * This parameter is to prevent we compute it again. * @param currentConfig The current configuration cached in {@link Activity#mCurrentConfig}. * It is {@code null} before the first config update from the server side. * @param newConfig The updated {@link Configuration} @@ -5935,9 +5932,10 @@ public final class ActivityThread extends ClientTransactionHandler * @return {@code true} if the config change should be reported to the Activity */ @VisibleForTesting - public static boolean shouldReportChange(int publicDiff, @Nullable Configuration currentConfig, + public static boolean shouldReportChange(@Nullable Configuration currentConfig, @NonNull Configuration newConfig, @Nullable SizeConfigurationBuckets sizeBuckets, int handledConfigChanges) { + final int publicDiff = currentConfig.diffPublicOnly(newConfig); // Don't report the change if there's no public diff between current and new config. if (publicDiff == 0) { return false; diff --git a/core/java/android/app/ActivityTransitionState.java b/core/java/android/app/ActivityTransitionState.java index 877e7d3b3bf7..57dacd024ba1 100644 --- a/core/java/android/app/ActivityTransitionState.java +++ b/core/java/android/app/ActivityTransitionState.java @@ -263,6 +263,11 @@ class ActivityTransitionState { // After orientation change, the onResume can come in before the top Activity has // left, so if the Activity is not top, wait a second for the top Activity to exit. if (mEnterTransitionCoordinator == null || activity.isTopOfTask()) { + if (mEnterTransitionCoordinator != null) { + mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> { + mEnterTransitionCoordinator = null; + }); + } restoreExitedViews(); restoreReenteringViews(); } else { @@ -271,6 +276,11 @@ class ActivityTransitionState { public void run() { if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isWaitingForRemoteExit()) { + if (mEnterTransitionCoordinator != null) { + mEnterTransitionCoordinator.runAfterTransitionsComplete(() -> { + mEnterTransitionCoordinator = null; + }); + } restoreExitedViews(); restoreReenteringViews(); } else if (mEnterTransitionCoordinator.isReturning()) { diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 56f8760f6059..f0e14483d98a 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -53,6 +53,7 @@ public class BroadcastOptions extends ComponentOptions { private String[] mRequireNoneOfPermissions; private long mRequireCompatChangeId = CHANGE_INVALID; private boolean mRequireCompatChangeEnabled = true; + private boolean mIsAlarmBroadcast = false; private long mIdForResponseEvent; /** @@ -149,6 +150,13 @@ public class BroadcastOptions extends ComponentOptions { "android:broadcast.requireCompatChangeEnabled"; /** + * Corresponds to {@link #setAlarmBroadcast(boolean)} + * @hide + */ + public static final String KEY_ALARM_BROADCAST = + "android:broadcast.is_alarm"; + + /** * @hide * @deprecated Use {@link android.os.PowerExemptionManager# * TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED} instead. @@ -207,6 +215,7 @@ public class BroadcastOptions extends ComponentOptions { mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID); mRequireCompatChangeEnabled = opts.getBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, true); mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT); + mIsAlarmBroadcast = opts.getBoolean(KEY_ALARM_BROADCAST, false); } /** @@ -498,6 +507,27 @@ public class BroadcastOptions extends ComponentOptions { mRequireCompatChangeEnabled = true; } + /** + * When set, this broadcast will be understood as having originated from an + * alarm going off. Only the OS itself can use this option; uses by other + * senders will be ignored. + * @hide + * + * @param senderIsAlarm Whether the broadcast is alarm-triggered. + */ + public void setAlarmBroadcast(boolean senderIsAlarm) { + mIsAlarmBroadcast = senderIsAlarm; + } + + /** + * Did this broadcast originate from an alarm triggering? + * @return true if this broadcast is an alarm message, false otherwise + * @hide + */ + public boolean isAlarmBroadcast() { + return mIsAlarmBroadcast; + } + /** {@hide} */ public long getRequireCompatChangeId() { return mRequireCompatChangeId; @@ -560,6 +590,9 @@ public class BroadcastOptions extends ComponentOptions { b.putInt(KEY_TEMPORARY_APP_ALLOWLIST_REASON_CODE, mTemporaryAppAllowlistReasonCode); b.putString(KEY_TEMPORARY_APP_ALLOWLIST_REASON, mTemporaryAppAllowlistReason); } + if (mIsAlarmBroadcast) { + b.putBoolean(KEY_ALARM_BROADCAST, true); + } if (mMinManifestReceiverApiLevel != 0) { b.putInt(KEY_MIN_MANIFEST_RECEIVER_API_LEVEL, mMinManifestReceiverApiLevel); } diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl index 7aeb2b26dd15..f5e5cda9c639 100644 --- a/core/java/android/app/IActivityClientController.aidl +++ b/core/java/android/app/IActivityClientController.aidl @@ -78,6 +78,7 @@ interface IActivityClientController { boolean willActivityBeVisible(in IBinder token); int getDisplayId(in IBinder activityToken); int getTaskForActivity(in IBinder token, in boolean onlyRoot); + int getTaskWindowingMode(in IBinder activityToken); IBinder getActivityTokenBelow(IBinder token); ComponentName getCallingActivity(in IBinder token); String getCallingPackage(in IBinder token); diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index 8984c4292023..556058b567f9 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -783,6 +783,17 @@ public class Instrumentation { return null; } + /** + * This is called after starting an Activity and provides the result code that defined in + * {@link ActivityManager}, like {@link ActivityManager#START_SUCCESS}. + * + * @param result the result code that returns after starting an Activity. + * @param bOptions the bundle generated from {@link ActivityOptions} that originally + * being used to start the Activity. + * @hide + */ + public void onStartActivityResult(int result, @NonNull Bundle bOptions) {} + final boolean match(Context who, Activity activity, Intent intent) { @@ -1344,6 +1355,28 @@ public class Instrumentation { return apk.getAppFactory(); } + /** + * This should be called before {@link #checkStartActivityResult(int, Object)}, because + * exceptions might be thrown while checking the results. + */ + private void notifyStartActivityResult(int result, @Nullable Bundle options) { + if (mActivityMonitors == null) { + return; + } + synchronized (mSync) { + final int size = mActivityMonitors.size(); + for (int i = 0; i < size; i++) { + final ActivityMonitor am = mActivityMonitors.get(i); + if (am.ignoreMatchingSpecificIntents()) { + if (options == null) { + options = ActivityOptions.makeBasic().toBundle(); + } + am.onStartActivityResult(result, options); + } + } + } + } + private void prePerformCreate(Activity activity) { if (mWaitingActivities != null) { synchronized (mSync) { @@ -1802,6 +1835,7 @@ public class Instrumentation { who.getOpPackageName(), who.getAttributionTag(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options); + notifyStartActivityResult(result, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); @@ -1876,6 +1910,7 @@ public class Instrumentation { int result = ActivityTaskManager.getService().startActivities(whoThread, who.getOpPackageName(), who.getAttributionTag(), intents, resolvedTypes, token, options, userId); + notifyStartActivityResult(result, options); checkStartActivityResult(result, intents[0]); return result; } catch (RemoteException e) { @@ -1947,6 +1982,7 @@ public class Instrumentation { who.getOpPackageName(), who.getAttributionTag(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, target, requestCode, 0, null, options); + notifyStartActivityResult(result, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); @@ -2017,6 +2053,7 @@ public class Instrumentation { who.getOpPackageName(), who.getAttributionTag(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, resultWho, requestCode, 0, null, options, user.getIdentifier()); + notifyStartActivityResult(result, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); @@ -2068,6 +2105,7 @@ public class Instrumentation { token, target != null ? target.mEmbeddedID : null, requestCode, 0, null, options, ignoreTargetSecurity, userId); + notifyStartActivityResult(result, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); @@ -2115,6 +2153,7 @@ public class Instrumentation { int result = appTask.startActivity(whoThread.asBinder(), who.getOpPackageName(), who.getAttributionTag(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), options); + notifyStartActivityResult(result, options); checkStartActivityResult(result, intent); } catch (RemoteException e) { throw new RuntimeException("Failure from system", e); diff --git a/core/java/android/app/search/SearchAction.java b/core/java/android/app/search/SearchAction.java index 9e40e7ebaef0..0c4508a4fb8d 100644 --- a/core/java/android/app/search/SearchAction.java +++ b/core/java/android/app/search/SearchAction.java @@ -67,7 +67,7 @@ public final class SearchAction implements Parcelable { private final UserHandle mUserHandle; @Nullable - private Bundle mExtras; + private final Bundle mExtras; SearchAction(Parcel in) { mId = in.readString(); @@ -99,7 +99,7 @@ public final class SearchAction implements Parcelable { mPendingIntent = pendingIntent; mIntent = intent; mUserHandle = userHandle; - mExtras = extras; + mExtras = extras != null ? extras : new Bundle(); if (mPendingIntent == null && mIntent == null) { throw new IllegalStateException("At least one type of intent should be available."); diff --git a/core/java/android/app/search/SearchTarget.java b/core/java/android/app/search/SearchTarget.java index a590a5d7b767..a3874f7cb007 100644 --- a/core/java/android/app/search/SearchTarget.java +++ b/core/java/android/app/search/SearchTarget.java @@ -185,7 +185,7 @@ public final class SearchTarget implements Parcelable { mShortcutInfo = shortcutInfo; mAppWidgetProviderInfo = appWidgetProviderInfo; mSliceUri = sliceUri; - mExtras = extras; + mExtras = extras != null ? extras : new Bundle(); int published = 0; if (mSearchAction != null) published++; diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 18c638112480..a432b8dec2cb 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -1130,7 +1130,9 @@ public class AppWidgetManager { * @param intent The intent of the service which will be providing the data to the * RemoteViewsAdapter. * @param connection The callback interface to be notified when a connection is made or lost. - * @param flags Flags used for binding to the service + * @param flags Flags used for binding to the service. Currently only + * {@link Context#BIND_AUTO_CREATE} and + * {@link Context#BIND_FOREGROUND_SERVICE_WHILE_AWAKE} are supported. * * @see Context#getServiceDispatcher(ServiceConnection, Handler, int) * @hide diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java index d41cda102103..85af87722ed2 100644 --- a/core/java/android/content/ClipboardManager.java +++ b/core/java/android/content/ClipboardManager.java @@ -129,7 +129,11 @@ public class ClipboardManager extends android.text.ClipboardManager { try { Objects.requireNonNull(clip); clip.prepareToLeaveProcess(true); - mService.setPrimaryClip(clip, mContext.getOpPackageName(), mContext.getUserId()); + mService.setPrimaryClip( + clip, + mContext.getOpPackageName(), + mContext.getAttributionTag(), + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -154,7 +158,11 @@ public class ClipboardManager extends android.text.ClipboardManager { Objects.requireNonNull(sourcePackage); clip.prepareToLeaveProcess(true); mService.setPrimaryClipAsPackage( - clip, mContext.getOpPackageName(), mContext.getUserId(), sourcePackage); + clip, + mContext.getOpPackageName(), + mContext.getAttributionTag(), + mContext.getUserId(), + sourcePackage); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -167,7 +175,10 @@ public class ClipboardManager extends android.text.ClipboardManager { */ public void clearPrimaryClip() { try { - mService.clearPrimaryClip(mContext.getOpPackageName(), mContext.getUserId()); + mService.clearPrimaryClip( + mContext.getOpPackageName(), + mContext.getAttributionTag(), + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -183,24 +194,29 @@ public class ClipboardManager extends android.text.ClipboardManager { */ public @Nullable ClipData getPrimaryClip() { try { - return mService.getPrimaryClip(mContext.getOpPackageName(), mContext.getUserId()); + return mService.getPrimaryClip( + mContext.getOpPackageName(), + mContext.getAttributionTag(), + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Returns a description of the current primary clip on the clipboard - * but not a copy of its data. + * Returns a description of the current primary clip on the clipboard but not a copy of its + * data. * - * <em>If the application is not the default IME or does not have input focus this return + * <p><em>If the application is not the default IME or does not have input focus this return * {@code null}.</em> * * @see #setPrimaryClip(ClipData) */ public @Nullable ClipDescription getPrimaryClipDescription() { try { - return mService.getPrimaryClipDescription(mContext.getOpPackageName(), + return mService.getPrimaryClipDescription( + mContext.getOpPackageName(), + mContext.getAttributionTag(), mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -215,7 +231,10 @@ public class ClipboardManager extends android.text.ClipboardManager { */ public boolean hasPrimaryClip() { try { - return mService.hasPrimaryClip(mContext.getOpPackageName(), mContext.getUserId()); + return mService.hasPrimaryClip( + mContext.getOpPackageName(), + mContext.getAttributionTag(), + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -226,7 +245,9 @@ public class ClipboardManager extends android.text.ClipboardManager { if (mPrimaryClipChangedListeners.isEmpty()) { try { mService.addPrimaryClipChangedListener( - mPrimaryClipChangedServiceListener, mContext.getOpPackageName(), + mPrimaryClipChangedServiceListener, + mContext.getOpPackageName(), + mContext.getAttributionTag(), mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -242,7 +263,9 @@ public class ClipboardManager extends android.text.ClipboardManager { if (mPrimaryClipChangedListeners.isEmpty()) { try { mService.removePrimaryClipChangedListener( - mPrimaryClipChangedServiceListener, mContext.getOpPackageName(), + mPrimaryClipChangedServiceListener, + mContext.getOpPackageName(), + mContext.getAttributionTag(), mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -280,7 +303,10 @@ public class ClipboardManager extends android.text.ClipboardManager { @Deprecated public boolean hasText() { try { - return mService.hasClipboardText(mContext.getOpPackageName(), mContext.getUserId()); + return mService.hasClipboardText( + mContext.getOpPackageName(), + mContext.getAttributionTag(), + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -297,7 +323,10 @@ public class ClipboardManager extends android.text.ClipboardManager { @RequiresPermission(Manifest.permission.SET_CLIP_SOURCE) public String getPrimaryClipSource() { try { - return mService.getPrimaryClipSource(mContext.getOpPackageName(), mContext.getUserId()); + return mService.getPrimaryClipSource( + mContext.getOpPackageName(), + mContext.getAttributionTag(), + mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/content/IClipboard.aidl b/core/java/android/content/IClipboard.aidl index 102b8e798a5c..46ece2bc3f5e 100644 --- a/core/java/android/content/IClipboard.aidl +++ b/core/java/android/content/IClipboard.aidl @@ -26,22 +26,22 @@ import android.content.IOnPrimaryClipChangedListener; * {@hide} */ interface IClipboard { - void setPrimaryClip(in ClipData clip, String callingPackage, int userId); - void setPrimaryClipAsPackage(in ClipData clip, String callingPackage, int userId, + void setPrimaryClip(in ClipData clip, String callingPackage, String attributionTag, int userId); + void setPrimaryClipAsPackage(in ClipData clip, String callingPackage, String attributionTag, int userId, String sourcePackage); - void clearPrimaryClip(String callingPackage, int userId); - ClipData getPrimaryClip(String pkg, int userId); - ClipDescription getPrimaryClipDescription(String callingPackage, int userId); - boolean hasPrimaryClip(String callingPackage, int userId); + void clearPrimaryClip(String callingPackage, String attributionTag, int userId); + ClipData getPrimaryClip(String pkg, String attributionTag, int userId); + ClipDescription getPrimaryClipDescription(String callingPackage, String attributionTag, int userId); + boolean hasPrimaryClip(String callingPackage, String attributionTag, int userId); void addPrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener, - String callingPackage, int userId); + String callingPackage, String attributionTag, int userId); void removePrimaryClipChangedListener(in IOnPrimaryClipChangedListener listener, - String callingPackage, int userId); + String callingPackage, String attributionTag, int userId); /** * Returns true if the clipboard contains text; false otherwise. */ - boolean hasClipboardText(String callingPackage, int userId); + boolean hasClipboardText(String callingPackage, String attributionTag, int userId); - String getPrimaryClipSource(String callingPackage, int userId); + String getPrimaryClipSource(String callingPackage, String attributionTag, int userId); } diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java index dac55ae08448..8db298ff8a85 100644 --- a/core/java/android/hardware/camera2/CameraCharacteristics.java +++ b/core/java/android/hardware/camera2/CameraCharacteristics.java @@ -1325,7 +1325,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri * flashlight brightness level via * {@link android.hardware.camera2.CameraManager#turnOnTorchWithStrengthLevel }. * If this value is equal to 1, flashlight brightness control is not supported. - * The value for this key will be null for devices with no flash unit.</p> + * The value for this key will be null for devices with no flash unit. + * This level must be set to a safe value to prevent any burn out issues.</p> * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p> */ @PublicKey diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java index 4d0ba63d7759..336ef7ac78db 100644 --- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java @@ -1739,6 +1739,20 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { // abruptly. Log.w(TAG, "Output surface likely abandoned, dropping buffer!"); img.close(); + } catch (RuntimeException e) { + // NOTE: This is intended to catch RuntimeException from ImageReader.detachImage + // ImageReader.detachImage is not supposed to throw RuntimeExceptions but the + // bug went unchecked for a few years and now its behavior cannot be changed + // without breaking backwards compatibility. + + if (!e.getClass().equals(RuntimeException.class)) { + // re-throw any exceptions that aren't base RuntimeException since they are + // coming from elsewhere, and we shouldn't silently drop those. + throw e; + } + + Log.w(TAG, "Output surface likely abandoned, dropping buffer!"); + img.close(); } } } @@ -1773,9 +1787,23 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession { } try { reader.detachImage(img); - } catch (Exception e) { - Log.e(TAG, - "Failed to detach image!"); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to detach image!"); + img.close(); + return; + } catch (RuntimeException e) { + // NOTE: This is intended to catch RuntimeException from ImageReader.detachImage + // ImageReader.detachImage is not supposed to throw RuntimeExceptions but the + // bug went unchecked for a few years and now its behavior cannot be changed + // without breaking backwards compatibility. + + if (!e.getClass().equals(RuntimeException.class)) { + // re-throw any exceptions that aren't base RuntimeException since they are + // coming from elsewhere, and we shouldn't silently drop those. + throw e; + } + + Log.e(TAG, "Failed to detach image!"); img.close(); return; } diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java index 6cf5d60acec8..cbd806617a69 100644 --- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java +++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableArray.java @@ -45,46 +45,41 @@ public class MarshalQueryableArray<T> implements MarshalQueryable<T> { private static final boolean DEBUG = false; private static interface PrimitiveArrayFiller { - public void fillPosition(Object arr, int index, ByteBuffer buffer); + public void fillArray(Object arr, int size, ByteBuffer buffer); static PrimitiveArrayFiller getPrimitiveArrayFiller(Class<?> componentType) { if (componentType == int.class) { return new PrimitiveArrayFiller() { @Override - public void fillPosition(Object arr, int index, ByteBuffer buffer) { - int i = buffer.getInt(); - Array.setInt(arr, index, i); + public void fillArray(Object arr, int size, ByteBuffer buffer) { + buffer.asIntBuffer().get(int[].class.cast(arr), 0, size); } }; } else if (componentType == float.class) { return new PrimitiveArrayFiller() { @Override - public void fillPosition(Object arr, int index, ByteBuffer buffer) { - float i = buffer.getFloat(); - Array.setFloat(arr, index, i); + public void fillArray(Object arr, int size, ByteBuffer buffer) { + buffer.asFloatBuffer().get(float[].class.cast(arr), 0, size); } }; } else if (componentType == long.class) { return new PrimitiveArrayFiller() { @Override - public void fillPosition(Object arr, int index, ByteBuffer buffer) { - long i = buffer.getLong(); - Array.setLong(arr, index, i); + public void fillArray(Object arr, int size, ByteBuffer buffer) { + buffer.asLongBuffer().get(long[].class.cast(arr), 0, size); } }; } else if (componentType == double.class) { return new PrimitiveArrayFiller() { @Override - public void fillPosition(Object arr, int index, ByteBuffer buffer) { - double i = buffer.getDouble(); - Array.setDouble(arr, index, i); + public void fillArray(Object arr, int size, ByteBuffer buffer) { + buffer.asDoubleBuffer().get(double[].class.cast(arr), 0, size); } }; } else if (componentType == byte.class) { return new PrimitiveArrayFiller() { @Override - public void fillPosition(Object arr, int index, ByteBuffer buffer) { - byte i = buffer.get(); - Array.setByte(arr, index, i); + public void fillArray(Object arr, int size, ByteBuffer buffer) { + buffer.get(byte[].class.cast(arr), 0, size); } }; } @@ -93,13 +88,6 @@ public class MarshalQueryableArray<T> implements MarshalQueryable<T> { } }; - static void unmarshalPrimitiveArray(Object arr, int size, ByteBuffer buffer, - PrimitiveArrayFiller filler) { - for (int i = 0; i < size; i++) { - filler.fillPosition(arr, i, buffer); - } - } - private class MarshalerArray extends Marshaler<T> { private final Class<T> mClass; private final Marshaler<?> mComponentMarshaler; @@ -150,8 +138,8 @@ public class MarshalQueryableArray<T> implements MarshalQueryable<T> { array = Array.newInstance(mComponentClass, arraySize); if (isUnwrappedPrimitiveClass(mComponentClass) && mComponentClass == getPrimitiveTypeClass(mNativeType)) { - unmarshalPrimitiveArray(array, arraySize, buffer, - PrimitiveArrayFiller.getPrimitiveArrayFiller(mComponentClass)); + PrimitiveArrayFiller.getPrimitiveArrayFiller(mComponentClass).fillArray(array, + arraySize, buffer); } else { for (int i = 0; i < arraySize; ++i) { Object elem = mComponentMarshaler.unmarshal(buffer); diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java index e8e4fc988937..f2525d17e30a 100644 --- a/core/java/android/hardware/radio/ProgramList.java +++ b/core/java/android/hardware/radio/ProgramList.java @@ -22,10 +22,11 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import android.util.ArrayMap; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; @@ -41,7 +42,7 @@ public final class ProgramList implements AutoCloseable { private final Object mLock = new Object(); private final Map<ProgramSelector.Identifier, RadioManager.ProgramInfo> mPrograms = - new HashMap<>(); + new ArrayMap<>(); private final List<ListCallback> mListCallbacks = new ArrayList<>(); private final List<OnCompleteListener> mOnCompleteListeners = new ArrayList<>(); @@ -184,8 +185,14 @@ public final class ProgramList implements AutoCloseable { listCallbacksCopied = new ArrayList<>(mListCallbacks); if (chunk.isPurge()) { - for (ProgramSelector.Identifier id : mPrograms.keySet()) { - removeLocked(id, removedList); + Iterator<Map.Entry<ProgramSelector.Identifier, RadioManager.ProgramInfo>> + programsIterator = mPrograms.entrySet().iterator(); + while (programsIterator.hasNext()) { + RadioManager.ProgramInfo removed = programsIterator.next().getValue(); + if (removed != null) { + removedList.add(removed.getSelector().getPrimaryId()); + } + programsIterator.remove(); } } diff --git a/core/java/android/view/IDisplayWindowInsetsController.aidl b/core/java/android/view/IDisplayWindowInsetsController.aidl index f4a0dfaa4432..1940042a1052 100644 --- a/core/java/android/view/IDisplayWindowInsetsController.aidl +++ b/core/java/android/view/IDisplayWindowInsetsController.aidl @@ -16,6 +16,7 @@ package android.view; +import android.content.ComponentName; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InsetsVisibilities; @@ -30,10 +31,11 @@ oneway interface IDisplayWindowInsetsController { /** * Called when top focused window changes to determine whether or not to take over insets * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false. - * @param packageName: Passes the top package name + * @param component: Passes the top application component in the focused window. * @param requestedVisibilities The insets visibilities requested by the focussed window. */ - void topFocusedWindowChanged(String packageName, in InsetsVisibilities insetsVisibilities); + void topFocusedWindowChanged(in ComponentName component, + in InsetsVisibilities insetsVisibilities); /** * @see IWindow#insetsChanged diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 61f524f51786..c4d307073d12 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -80,11 +80,6 @@ interface IRecentsAnimationController { void setAnimationTargetsBehindSystemBars(boolean behindSystemBars); /** - * Hides the current input method if one is showing. - */ - void hideCurrentInputMethod(); - - /** * Clean up the screenshot of previous task which was created during recents animation that * was cancelled by a stack order change. * diff --git a/core/java/android/view/InsetsFrameProvider.java b/core/java/android/view/InsetsFrameProvider.java index 9e66f54321cc..eb8687c47bed 100644 --- a/core/java/android/view/InsetsFrameProvider.java +++ b/core/java/android/view/InsetsFrameProvider.java @@ -16,10 +16,14 @@ package android.view; +import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT; + import android.graphics.Insets; +import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; +import java.util.Arrays; import java.util.Objects; /** @@ -52,7 +56,9 @@ public class InsetsFrameProvider implements Parcelable { public static final int SOURCE_FRAME = 2; private static final int HAS_INSETS_SIZE = 1; - private static final int HAS_IME_INSETS_SIZE = 2; + private static final int HAS_INSETS_SIZE_OVERRIDE = 2; + + private static Rect sTmpRect = new Rect(); /** * The type of insets to provide. @@ -77,29 +83,25 @@ public class InsetsFrameProvider implements Parcelable { public Insets insetsSize = null; /** - * The provided frame based on the source frame. The result will be used as the insets - * size to IME window. Only one side should be set. + * If null, the size set in insetsSize will be applied to all window types. If it contains + * element of some types, the insets reported to the window with that types will be overridden. */ - public Insets imeInsetsSize = null; + public InsetsSizeOverride[] insetsSizeOverrides = null; public InsetsFrameProvider(int type) { this(type, SOURCE_FRAME, null, null); } public InsetsFrameProvider(int type, Insets insetsSize) { - this(type, SOURCE_FRAME, insetsSize, insetsSize); - } - - public InsetsFrameProvider(int type, Insets insetsSize, Insets imeInsetsSize) { - this(type, SOURCE_FRAME, insetsSize, imeInsetsSize); + this(type, SOURCE_FRAME, insetsSize, null); } public InsetsFrameProvider(int type, int source, Insets insetsSize, - Insets imeInsetsSize) { + InsetsSizeOverride[] insetsSizeOverride) { this.type = type; this.source = source; this.insetsSize = insetsSize; - this.imeInsetsSize = imeInsetsSize; + this.insetsSizeOverrides = insetsSizeOverride; } @Override @@ -127,8 +129,8 @@ public class InsetsFrameProvider implements Parcelable { if (insetsSize != null) { sb.append(", insetsSize=").append(insetsSize); } - if (imeInsetsSize != null) { - sb.append(", imeInsetsSize=").append(imeInsetsSize); + if (insetsSizeOverrides != null) { + sb.append(", insetsSizeOverrides=").append(Arrays.toString(insetsSizeOverrides)); } sb.append("}"); return sb.toString(); @@ -141,8 +143,8 @@ public class InsetsFrameProvider implements Parcelable { if ((insetsSizeModified & HAS_INSETS_SIZE) != 0) { insetsSize = Insets.CREATOR.createFromParcel(in); } - if ((insetsSizeModified & HAS_IME_INSETS_SIZE) != 0) { - imeInsetsSize = Insets.CREATOR.createFromParcel(in); + if ((insetsSizeModified & HAS_INSETS_SIZE_OVERRIDE) != 0) { + insetsSizeOverrides = in.createTypedArray(InsetsSizeOverride.CREATOR); } } @@ -152,8 +154,8 @@ public class InsetsFrameProvider implements Parcelable { if (insetsSize != null) { insetsSizeModified |= HAS_INSETS_SIZE; } - if (imeInsetsSize != null) { - insetsSizeModified |= HAS_IME_INSETS_SIZE; + if (insetsSizeOverrides != null) { + insetsSizeModified |= HAS_INSETS_SIZE_OVERRIDE; } out.writeInt(insetsSizeModified); out.writeInt(type); @@ -161,8 +163,8 @@ public class InsetsFrameProvider implements Parcelable { if (insetsSize != null) { insetsSize.writeToParcel(out, flags); } - if (imeInsetsSize != null) { - imeInsetsSize.writeToParcel(out, flags); + if (insetsSizeOverrides != null) { + out.writeTypedArray(insetsSizeOverrides, flags); } } @@ -177,16 +179,12 @@ public class InsetsFrameProvider implements Parcelable { InsetsFrameProvider other = (InsetsFrameProvider) o; return type == other.type && source == other.source && Objects.equals(insetsSize, other.insetsSize) - && Objects.equals(imeInsetsSize, other.imeInsetsSize); + && Arrays.equals(insetsSizeOverrides, other.insetsSizeOverrides); } @Override public int hashCode() { - int result = type; - result = 31 * result + source; - result = 31 * result + (insetsSize != null ? insetsSize.hashCode() : 0); - result = 31 * result + (imeInsetsSize != null ? imeInsetsSize.hashCode() : 0); - return result; + return Objects.hash(type, source, insetsSize, Arrays.hashCode(insetsSizeOverrides)); } public static final @android.annotation.NonNull Parcelable.Creator<InsetsFrameProvider> @@ -201,5 +199,96 @@ public class InsetsFrameProvider implements Parcelable { return new InsetsFrameProvider[size]; } }; + + public static void calculateInsetsFrame(Rect displayFrame, Rect containerBounds, + Rect displayCutoutSafe, Rect inOutFrame, int source, Insets insetsSize, + @WindowManager.LayoutParams.PrivateFlags int privateFlags) { + boolean extendByCutout = false; + if (source == InsetsFrameProvider.SOURCE_DISPLAY) { + inOutFrame.set(displayFrame); + } else if (source == InsetsFrameProvider.SOURCE_CONTAINER_BOUNDS) { + inOutFrame.set(containerBounds); + } else { + extendByCutout = (privateFlags & PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT) != 0; + } + if (insetsSize == null) { + return; + } + // Only one side of the provider shall be applied. Check in the order of left - top - + // right - bottom, only the first non-zero value will be applied. + if (insetsSize.left != 0) { + inOutFrame.right = inOutFrame.left + insetsSize.left; + } else if (insetsSize.top != 0) { + inOutFrame.bottom = inOutFrame.top + insetsSize.top; + } else if (insetsSize.right != 0) { + inOutFrame.left = inOutFrame.right - insetsSize.right; + } else if (insetsSize.bottom != 0) { + inOutFrame.top = inOutFrame.bottom - insetsSize.bottom; + } else { + inOutFrame.setEmpty(); + } + + if (extendByCutout) { + WindowLayout.extendFrameByCutout(displayCutoutSafe, displayFrame, inOutFrame, sTmpRect); + } + } + + /** + * Class to describe the insets size to be provided to window with specific window type. If not + * used, same insets size will be sent as instructed in the insetsSize and source. + */ + public static class InsetsSizeOverride implements Parcelable { + public final int windowType; + public Insets insetsSize; + + protected InsetsSizeOverride(Parcel in) { + windowType = in.readInt(); + insetsSize = in.readParcelable(null, android.graphics.Insets.class); + } + + public InsetsSizeOverride(int type, Insets size) { + windowType = type; + insetsSize = size; + } + + public static final Creator<InsetsSizeOverride> CREATOR = + new Creator<InsetsSizeOverride>() { + @Override + public InsetsSizeOverride createFromParcel(Parcel in) { + return new InsetsSizeOverride(in); + } + + @Override + public InsetsSizeOverride[] newArray(int size) { + return new InsetsSizeOverride[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeInt(windowType); + out.writeParcelable(insetsSize, flags); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(32); + sb.append("TypedInsetsSize: {"); + sb.append("windowType=").append(windowType); + sb.append(", insetsSize=").append(insetsSize); + sb.append("}"); + return sb.toString(); + } + + @Override + public int hashCode() { + return Objects.hash(windowType, insetsSize); + } + } } diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index 11e02e03c4e4..6a0ec3368f19 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -909,6 +909,11 @@ public class InsetsState implements Parcelable { if (source == null && otherSource == null) { continue; } + if (excludeInvisibleImeFrames && i == ITYPE_IME + && ((source == null && !otherSource.isVisible()) + || (otherSource == null && !source.isVisible()))) { + continue; + } if (source == null || otherSource == null) { return false; } diff --git a/core/java/android/view/RemoteAccessibilityController.java b/core/java/android/view/RemoteAccessibilityController.java index 28b567d94e6e..b0911d789fe1 100644 --- a/core/java/android/view/RemoteAccessibilityController.java +++ b/core/java/android/view/RemoteAccessibilityController.java @@ -24,6 +24,8 @@ import android.os.RemoteException; import android.util.Log; import android.view.accessibility.IAccessibilityEmbeddedConnection; +import java.lang.ref.WeakReference; + class RemoteAccessibilityController { private static final String TAG = "RemoteAccessibilityController"; private int mHostId; @@ -80,12 +82,17 @@ class RemoteAccessibilityController { /** * Wrapper of accessibility embedded connection for embedded view hierarchy. */ - private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient { + private static final class RemoteAccessibilityEmbeddedConnection + implements IBinder.DeathRecipient { + private final WeakReference<RemoteAccessibilityController> mController; private final IAccessibilityEmbeddedConnection mConnection; private final IBinder mLeashToken; - RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection, + RemoteAccessibilityEmbeddedConnection( + RemoteAccessibilityController controller, + IAccessibilityEmbeddedConnection connection, IBinder leashToken) { + mController = new WeakReference<>(controller); mConnection = connection; mLeashToken = leashToken; } @@ -109,9 +116,13 @@ class RemoteAccessibilityController { @Override public void binderDied() { unlinkToDeath(); - runOnUiThread(() -> { - if (mConnectionWrapper == this) { - mConnectionWrapper = null; + RemoteAccessibilityController controller = mController.get(); + if (controller == null) { + return; + } + controller.runOnUiThread(() -> { + if (controller.mConnectionWrapper == this) { + controller.mConnectionWrapper = null; } }); } @@ -128,7 +139,7 @@ class RemoteAccessibilityController { } if (connection != null && leashToken != null) { mConnectionWrapper = - new RemoteAccessibilityEmbeddedConnection(connection, leashToken); + new RemoteAccessibilityEmbeddedConnection(this, connection, leashToken); mConnectionWrapper.linkToDeath(); } } catch (RemoteException e) { diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java index d70de74ee75f..c50f70a08063 100644 --- a/core/java/android/view/ScrollCaptureConnection.java +++ b/core/java/android/view/ScrollCaptureConnection.java @@ -214,7 +214,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub imple @BinderThread @Override - public void close() { + public synchronized void close() { Trace.instantForTrack(TRACE_TAG_GRAPHICS, TRACE_TRACK, "close"); if (mActive) { Log.w(TAG, "close(): capture session still active! Ending now."); diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 785735c2b1e1..c97eb73c120b 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -25,11 +25,11 @@ import android.graphics.BLASTBufferQueue; import android.graphics.FrameInfo; import android.graphics.HardwareRenderer; import android.graphics.Picture; -import android.graphics.Point; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; import android.os.Trace; +import android.util.DisplayMetrics; import android.util.Log; import android.view.Surface.OutOfResourcesException; import android.view.View.AttachInfo; @@ -596,11 +596,18 @@ public final class ThreadedRenderer extends HardwareRenderer { */ void setLightCenter(AttachInfo attachInfo) { // Adjust light position for window offsets. - final Point displaySize = attachInfo.mPoint; - attachInfo.mDisplay.getRealSize(displaySize); - final float lightX = displaySize.x / 2f - attachInfo.mWindowLeft; + DisplayMetrics displayMetrics = new DisplayMetrics(); + attachInfo.mDisplay.getRealMetrics(displayMetrics); + final float lightX = displayMetrics.widthPixels / 2f - attachInfo.mWindowLeft; final float lightY = mLightY - attachInfo.mWindowTop; - setLightSourceGeometry(lightX, lightY, mLightZ, mLightRadius); + // To prevent shadow distortion on larger screens, scale the z position of the light source + // relative to the smallest screen dimension. + final float zRatio = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels) + / (450f * displayMetrics.density); + final float zWeightedAdjustment = (zRatio + 2) / 3f; + final float lightZ = mLightZ * zWeightedAdjustment; + + setLightSourceGeometry(lightX, lightY, lightZ, mLightRadius); } /** @@ -849,12 +856,18 @@ public final class ThreadedRenderer extends HardwareRenderer { public void setLightCenter(final Display display, final int windowLeft, final int windowTop) { // Adjust light position for window offsets. - final Point displaySize = new Point(); - display.getRealSize(displaySize); - final float lightX = displaySize.x / 2f - windowLeft; + DisplayMetrics displayMetrics = new DisplayMetrics(); + display.getRealMetrics(displayMetrics); + final float lightX = displayMetrics.widthPixels / 2f - windowLeft; final float lightY = mLightY - windowTop; - - setLightSourceGeometry(lightX, lightY, mLightZ, mLightRadius); + // To prevent shadow distortion on larger screens, scale the z position of the light + // source relative to the smallest screen dimension. + final float zRatio = Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels) + / (450f * displayMetrics.density); + final float zWeightedAdjustment = (zRatio + 2) / 3f; + final float lightZ = mLightZ * zWeightedAdjustment; + + setLightSourceGeometry(lightX, lightY, lightZ, mLightRadius); } public RenderNode getRootNode() { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 3811d032a384..7be8dffdf023 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2801,6 +2801,7 @@ public final class ViewRootImpl implements ViewParent, if (viewVisibilityChanged) { mAttachInfo.mWindowVisibility = viewVisibility; host.dispatchWindowVisibilityChanged(viewVisibility); + mAttachInfo.mTreeObserver.dispatchOnWindowVisibilityChange(viewVisibility); if (viewUserVisibilityChanged) { host.dispatchVisibilityAggregated(viewVisibility == View.VISIBLE); } @@ -3758,6 +3759,7 @@ public final class ViewRootImpl implements ViewParent, } } mFirstInputStage.onWindowFocusChanged(hasWindowFocus); + mOnBackInvokedDispatcher.onWindowFocusChanged(hasWindowFocus); // NOTE: there's no view visibility (appeared / disapparead) events when the windows focus // is lost, so we don't need to to force a flush - there might be other events such as diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index 5a99ab2f5bdb..ed8350afc109 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -43,6 +43,7 @@ public final class ViewTreeObserver { // Recursive listeners use CopyOnWriteArrayList private CopyOnWriteArrayList<OnWindowFocusChangeListener> mOnWindowFocusListeners; private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners; + private CopyOnWriteArrayList<OnWindowVisibilityChangeListener> mOnWindowVisibilityListeners; private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners; @UnsupportedAppUsage private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners; @@ -106,6 +107,21 @@ public final class ViewTreeObserver { } /** + * Interface definition for a callback to be invoked when the view hierarchy's window + * visibility changes. + * + * @hide + */ + public interface OnWindowVisibilityChangeListener { + /** + * Callback method to be invoked when the window visibility changes in the view tree. + * + * @param visibility The new visibility of the window. + */ + void onWindowVisibilityChanged(@View.Visibility int visibility); + } + + /** * Interface definition for a callback to be invoked when the focus state within * the view tree changes. */ @@ -386,6 +402,14 @@ public final class ViewTreeObserver { } } + if (observer.mOnWindowVisibilityListeners != null) { + if (mOnWindowVisibilityListeners != null) { + mOnWindowVisibilityListeners.addAll(observer.mOnWindowVisibilityListeners); + } else { + mOnWindowVisibilityListeners = observer.mOnWindowVisibilityListeners; + } + } + if (observer.mOnGlobalFocusListeners != null) { if (mOnGlobalFocusListeners != null) { mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners); @@ -540,6 +564,49 @@ public final class ViewTreeObserver { } /** + * Register a callback to be invoked when the window visibility changes. + * + * @param listener The callback to add + * + * @throws IllegalStateException If {@link #isAlive()} returns false + * + * @hide + */ + public void addOnWindowVisibilityChangeListener( + @NonNull OnWindowVisibilityChangeListener listener) { + checkIsAlive(); + + if (mOnWindowVisibilityListeners == null) { + mOnWindowVisibilityListeners = + new CopyOnWriteArrayList<OnWindowVisibilityChangeListener>(); + } + + mOnWindowVisibilityListeners.add(listener); + } + + /** + * Remove a previously installed window visibility callback. + * + * @param victim The callback to remove + * + * @throws IllegalStateException If {@link #isAlive()} returns false + * + * @see #addOnWindowVisibilityChangeListener( + * android.view.ViewTreeObserver.OnWindowVisibilityChangeListener) + * + * @hide + */ + public void removeOnWindowVisibilityChangeListener( + @NonNull OnWindowVisibilityChangeListener victim) { + checkIsAlive(); + if (mOnWindowVisibilityListeners == null) { + return; + } + + mOnWindowVisibilityListeners.remove(victim); + } + + /* * Register a callback to be invoked when the focus state within the view tree changes. * * @param listener The callback to add @@ -1026,6 +1093,23 @@ public final class ViewTreeObserver { } /** + * Notifies registered listeners that window visibility has changed. + */ + void dispatchOnWindowVisibilityChange(int visibility) { + // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to + // perform the dispatching. The iterator is a safe guard against listeners that + // could mutate the list by calling the various add/remove methods. This prevents + // the array from being modified while we iterate it. + final CopyOnWriteArrayList<OnWindowVisibilityChangeListener> listeners = + mOnWindowVisibilityListeners; + if (listeners != null && listeners.size() > 0) { + for (OnWindowVisibilityChangeListener listener : listeners) { + listener.onWindowVisibilityChanged(visibility); + } + } + } + + /** * Notifies registered listeners that focus has changed. */ @UnsupportedAppUsage diff --git a/core/java/android/view/WindowLayout.java b/core/java/android/view/WindowLayout.java index 9b6b2b906e8e..57a0330e3c18 100644 --- a/core/java/android/view/WindowLayout.java +++ b/core/java/android/view/WindowLayout.java @@ -16,8 +16,6 @@ package android.view; -import static android.view.Gravity.DISPLAY_CLIP_HORIZONTAL; -import static android.view.Gravity.DISPLAY_CLIP_VERTICAL; import static android.view.InsetsState.ITYPE_IME; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; @@ -275,17 +273,9 @@ public class WindowLayout { Gravity.applyDisplay(attrs.gravity, outDisplayFrame, outFrame); } - if (extendedByCutout && !displayCutoutSafe.contains(outFrame)) { - mTempRect.set(outFrame); - - // Move the frame into displayCutoutSafe. - final int clipFlags = DISPLAY_CLIP_VERTICAL | DISPLAY_CLIP_HORIZONTAL; - Gravity.applyDisplay(attrs.gravity & ~clipFlags, displayCutoutSafe, + if (extendedByCutout) { + extendFrameByCutout(displayCutoutSafe, outDisplayFrame, outFrame, mTempRect); - - if (mTempRect.intersect(outDisplayFrame)) { - outFrame.union(mTempRect); - } } if (DEBUG) Log.d(TAG, "computeFrames " + attrs.getTitle() @@ -301,6 +291,21 @@ public class WindowLayout { + " requestedVisibilities=" + requestedVisibilities); } + public static void extendFrameByCutout(Rect displayCutoutSafe, + Rect displayFrame, Rect inOutFrame, Rect tempRect) { + if (displayCutoutSafe.contains(inOutFrame)) { + return; + } + tempRect.set(inOutFrame); + + // Move the frame into displayCutoutSafe. + Gravity.applyDisplay(0 /* gravity */, displayCutoutSafe, tempRect); + + if (tempRect.intersect(displayFrame)) { + inOutFrame.union(tempRect); + } + } + public static void computeSurfaceSize(WindowManager.LayoutParams attrs, Rect maxBounds, int requestedWidth, int requestedHeight, Rect winFrame, boolean dragResizing, Point outSurfaceSize) { diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 25445abefca2..d37756551db3 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -404,9 +404,10 @@ public final class WindowManagerGlobal { try { root.setView(view, wparams, panelParentView, userId); } catch (RuntimeException e) { + final int viewIndex = (index >= 0) ? index : (mViews.size() - 1); // BadTokenException or InvalidDisplayException, clean up. - if (index >= 0) { - removeViewLocked(index, true); + if (viewIndex >= 0) { + removeViewLocked(viewIndex, true); } throw e; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 23393ffe885c..2268bef2c1d9 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -6440,9 +6440,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void setText(CharSequence text, BufferType type) { setText(text, type, true, 0); - if (mCharWrapper != null) { - mCharWrapper.mChars = null; - } + // drop any potential mCharWrappper leaks + mCharWrapper = null; } @UnsupportedAppUsage @@ -6653,11 +6652,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * since the TextView has no way to know that the text * has changed and that it needs to invalidate and re-layout. * + * @throws NullPointerException if text is null + * @throws IndexOutOfBoundsException if start or start+len are not in 0 to text.length + * * @param text char array to be displayed * @param start start index in the char array * @param len length of char count after {@code start} */ - public final void setText(char[] text, int start, int len) { + public final void setText(/* @NonNull */ char[] text, int start, int len) { int oldlen = 0; if (start < 0 || len < 0 || start + len > text.length) { @@ -13888,16 +13890,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations { + @NonNull private char[] mChars; private int mStart, mLength; - public CharWrapper(char[] chars, int start, int len) { + CharWrapper(@NonNull char[] chars, int start, int len) { mChars = chars; mStart = start; mLength = len; } - /* package */ void set(char[] chars, int start, int len) { + /* package */ void set(@NonNull char[] chars, int start, int len) { mChars = chars; mStart = start; mLength = len; diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java index 5924844aa3b2..f1a052b61c59 100644 --- a/core/java/android/window/ImeOnBackInvokedDispatcher.java +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -81,8 +81,10 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc @OnBackInvokedDispatcher.Priority int priority, @NonNull OnBackInvokedCallback callback) { final Bundle bundle = new Bundle(); + // Always invoke back for ime without checking the window focus. final IOnBackInvokedCallback iCallback = - new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback); + new WindowOnBackInvokedDispatcher.OnBackInvokedCallbackWrapper(callback, + () -> true); bundle.putBinder(RESULT_KEY_CALLBACK, iCallback.asBinder()); bundle.putInt(RESULT_KEY_PRIORITY, priority); bundle.putInt(RESULT_KEY_ID, callback.hashCode()); diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java index c81184fb2383..1f3841ad8b58 100644 --- a/core/java/android/window/TransitionInfo.java +++ b/core/java/android/window/TransitionInfo.java @@ -110,8 +110,11 @@ public final class TransitionInfo implements Parcelable { /** The container is an input-method window. */ public static final int FLAG_IS_INPUT_METHOD = 1 << 8; + /** The container is ActivityEmbedding embedded. */ + public static final int FLAG_IS_EMBEDDED = 1 << 9; + /** 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 << 9; + public static final int FLAG_FIRST_CUSTOM = 1 << 10; /** @hide */ @IntDef(prefix = { "FLAG_" }, value = { @@ -125,6 +128,7 @@ public final class TransitionInfo implements Parcelable { FLAG_OCCLUDES_KEYGUARD, FLAG_DISPLAY_HAS_ALERT_WINDOWS, FLAG_IS_INPUT_METHOD, + FLAG_IS_EMBEDDED, FLAG_FIRST_CUSTOM }) public @interface ChangeFlags {} @@ -325,6 +329,9 @@ public final class TransitionInfo implements Parcelable { if ((flags & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) { sb.append((sb.length() == 0 ? "" : "|") + "DISPLAY_HAS_ALERT_WINDOWS"); } + if ((flags & FLAG_IS_EMBEDDED) != 0) { + sb.append((sb.length() == 0 ? "" : "|") + "IS_EMBEDDED"); + } if ((flags & FLAG_FIRST_CUSTOM) != 0) { sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM"); } diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index a87b91d87f1c..3bffa890122a 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -348,8 +348,10 @@ public final class WindowContainerTransaction implements Parcelable { * @param currentParent of the tasks to perform the operation no. * {@code null} will perform the operation on the display. * @param newParent for the tasks. {@code null} will perform the operation on the display. - * @param windowingModes of the tasks to reparent. - * @param activityTypes of the tasks to reparent. + * @param windowingModes of the tasks to reparent. {@code null} ignore this attribute when + * perform the operation. + * @param activityTypes of the tasks to reparent. {@code null} ignore this attribute when + * perform the operation. * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to * the bottom. */ diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index d147524d3b3d..02c5ebcc184e 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -32,6 +32,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Objects; import java.util.TreeMap; +import java.util.function.Supplier; /** * Provides window based implementation of {@link OnBackInvokedDispatcher}. @@ -64,6 +65,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { private final TreeMap<Integer, ArrayList<OnBackInvokedCallback>> mOnBackInvokedCallbacks = new TreeMap<>(); private final Checker mChecker; + private boolean mHasFocus; public WindowOnBackInvokedDispatcher(boolean applicationCallBackEnabled) { mChecker = new Checker(applicationCallBackEnabled); @@ -189,7 +191,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { .ImeOnBackInvokedCallback ? ((ImeOnBackInvokedDispatcher.ImeOnBackInvokedCallback) callback).getIOnBackInvokedCallback() - : new OnBackInvokedCallbackWrapper(callback); + : new OnBackInvokedCallbackWrapper(callback, this::hasFocus); callbackInfo = new OnBackInvokedCallbackInfo(iCallback, priority); } mWindowSession.setOnBackInvokedCallbackInfo(mWindow, callbackInfo); @@ -198,6 +200,17 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { } } + /** + * Called when window focus changed. + */ + public void onWindowFocusChanged(boolean hasFocus) { + mHasFocus = hasFocus; + } + + private boolean hasFocus() { + return mHasFocus; + } + public OnBackInvokedCallback getTopCallback() { if (mAllCallbacks.isEmpty()) { return null; @@ -221,9 +234,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { static class OnBackInvokedCallbackWrapper extends IOnBackInvokedCallback.Stub { private final WeakReference<OnBackInvokedCallback> mCallback; - - OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback) { + private final Supplier<Boolean> mHasFocus; + OnBackInvokedCallbackWrapper(@NonNull OnBackInvokedCallback callback, + @NonNull Supplier<Boolean> hasFocus) { mCallback = new WeakReference<>(callback); + mHasFocus = hasFocus; } @Override @@ -263,7 +278,10 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { if (callback == null) { return; } - + if (!mHasFocus.get()) { + Log.w(TAG, "Skip back invoke due to current focus has lost."); + return; + } callback.onBackInvoked(); }); } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 1dedec366b75..f691300960be 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -30,6 +30,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.SharedElementCallback; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionManager; @@ -101,7 +102,10 @@ import android.view.ViewGroup.LayoutParams; import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.animation.AccelerateInterpolator; +import android.view.animation.AlphaAnimation; +import android.view.animation.Animation; import android.view.animation.DecelerateInterpolator; +import android.view.animation.LinearInterpolator; import android.widget.Button; import android.widget.ImageView; import android.widget.Space; @@ -197,6 +201,8 @@ public class ChooserActivity extends ResolverActivity implements private static final String PLURALS_COUNT = "count"; private static final String PLURALS_FILE_NAME = "file_name"; + private static final String IMAGE_EDITOR_SHARED_ELEMENT = "screenshot_preview_image"; + private boolean mIsAppPredictorComponentAvailable; private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache; private Map<ChooserTarget, ShortcutInfo> mDirectShareShortcutInfoCache; @@ -250,6 +256,11 @@ public class ChooserActivity extends ResolverActivity implements private static final int DEFAULT_LIST_VIEW_UPDATE_DELAY_MS = 125; + private static final int URI_PERMISSION_INTENT_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION + | Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION; + @VisibleForTesting int mListViewUpdateDelayMs = DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.SHARESHEET_LIST_VIEW_UPDATE_DELAY, @@ -305,6 +316,8 @@ public class ChooserActivity extends ResolverActivity implements private boolean mRemoveSharedElements = false; + private View mContentView = null; + private class ContentPreviewCoordinator { private static final int IMAGE_FADE_IN_MILLIS = 150; private static final int IMAGE_LOAD_TIMEOUT = 1; @@ -990,6 +1003,7 @@ public class ChooserActivity extends ResolverActivity implements protected void onResume() { super.onResume(); Log.d(TAG, "onResume: " + getComponentName().flattenToShortString()); + maybeCancelFinishAnimation(); } @Override @@ -1085,6 +1099,10 @@ public class ChooserActivity extends ResolverActivity implements final ComponentName cn = getEditSharingComponent(); final Intent resolveIntent = new Intent(originalIntent); + // Retain only URI permission grant flags if present. Other flags may prevent the scene + // transition animation from running (i.e FLAG_ACTIVITY_NO_ANIMATION, + // FLAG_ACTIVITY_NEW_TASK, FLAG_ACTIVITY_NEW_DOCUMENT) but also not needed. + resolveIntent.setFlags(originalIntent.getFlags() & URI_PERMISSION_INTENT_FLAGS); resolveIntent.setComponent(cn); resolveIntent.setAction(Intent.ACTION_EDIT); final ResolveInfo ri = getPackageManager().resolveActivity( @@ -1101,7 +1119,6 @@ public class ChooserActivity extends ResolverActivity implements return dri; } - @VisibleForTesting protected TargetInfo getNearbySharingTarget(Intent originalIntent) { final ComponentName cn = getNearbySharingComponent(); @@ -1204,15 +1221,30 @@ public class ChooserActivity extends ResolverActivity implements "", -1, false); + View firstImgView = getFirstVisibleImgPreviewView(); // Action bar is user-independent, always start as primary - safelyStartActivityAsUser(ti, getPersonalProfileUserHandle()); - finish(); + if (firstImgView == null) { + safelyStartActivityAsUser(ti, getPersonalProfileUserHandle()); + finish(); + } else { + ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation( + this, firstImgView, IMAGE_EDITOR_SHARED_ELEMENT); + safelyStartActivityAsUser( + ti, getPersonalProfileUserHandle(), options.toBundle()); + startFinishAnimation(); + } } ); b.setId(R.id.chooser_edit_button); return b; } + @Nullable + private View getFirstVisibleImgPreviewView() { + View firstImage = findViewById(R.id.content_preview_image_1_large); + return firstImage != null && firstImage.isVisibleToUser() ? firstImage : null; + } + private void addActionButton(ViewGroup parent, Button b) { if (b == null) return; final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams( @@ -1560,6 +1592,14 @@ public class ChooserActivity extends ResolverActivity implements } @Override + protected void onStop() { + super.onStop(); + if (maybeCancelFinishAnimation()) { + finish(); + } + } + + @Override protected void onDestroy() { super.onDestroy(); @@ -2887,6 +2927,30 @@ public class ChooserActivity extends ResolverActivity implements .setSubtype(previewType)); } + private void startFinishAnimation() { + View rootView = findRootView(); + rootView.startAnimation(new FinishAnimation(this, rootView)); + } + + private boolean maybeCancelFinishAnimation() { + View rootView = findRootView(); + Animation animation = rootView.getAnimation(); + if (animation instanceof FinishAnimation) { + boolean hasEnded = animation.hasEnded(); + animation.cancel(); + rootView.clearAnimation(); + return !hasEnded; + } + return false; + } + + private View findRootView() { + if (mContentView == null) { + mContentView = findViewById(android.R.id.content); + } + return mContentView; + } + abstract static class ViewHolderBase extends RecyclerView.ViewHolder { private int mViewType; @@ -3987,6 +4051,66 @@ public class ChooserActivity extends ResolverActivity implements } } + /** + * Used in combination with the scene transition when launching the image editor + */ + private static class FinishAnimation extends AlphaAnimation implements + Animation.AnimationListener { + private Activity mActivity; + private View mRootView; + private final float mFromAlpha; + + FinishAnimation(Activity activity, View rootView) { + super(rootView.getAlpha(), 0.0f); + mActivity = activity; + mRootView = rootView; + mFromAlpha = rootView.getAlpha(); + setInterpolator(new LinearInterpolator()); + long duration = activity.getWindow().getTransitionBackgroundFadeDuration(); + setDuration(duration); + // The scene transition animation looks better when it's not overlapped with this + // fade-out animation thus the delay. + // It is most likely that the image editor will cause this activity to stop and this + // animation will be cancelled in the background without running (i.e. we'll animate + // only when this activity remains partially visible after the image editor launch). + setStartOffset(duration); + super.setAnimationListener(this); + } + + @Override + public void setAnimationListener(AnimationListener listener) { + throw new UnsupportedOperationException(); + } + + @Override + public void cancel() { + mRootView.setAlpha(mFromAlpha); + cleanup(); + super.cancel(); + } + + @Override + public void onAnimationStart(Animation animation) { + } + + @Override + public void onAnimationEnd(Animation animation) { + if (mActivity != null) { + mActivity.finish(); + cleanup(); + } + } + + @Override + public void onAnimationRepeat(Animation animation) { + } + + private void cleanup() { + mActivity = null; + mRootView = null; + } + } + @Override protected void maybeLogProfileChange() { getChooserActivityLogger().logShareheetProfileChanged(); diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 40429c609150..e9e437fda8f2 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1314,7 +1314,7 @@ public class ResolverActivity extends Activity implements StrictMode.disableDeathOnFileUriExposure(); try { UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle(); - safelyStartActivityInternal(cti, currentUserHandle); + safelyStartActivityInternal(cti, currentUserHandle, null); } finally { StrictMode.enableDeathOnFileUriExposure(); } @@ -1327,18 +1327,23 @@ public class ResolverActivity extends Activity implements */ @VisibleForTesting public void safelyStartActivityAsUser(TargetInfo cti, UserHandle user) { + safelyStartActivityAsUser(cti, user, null); + } + + protected void safelyStartActivityAsUser( + TargetInfo cti, UserHandle user, @Nullable Bundle options) { // We're dispatching intents that might be coming from legacy apps, so // don't kill ourselves. StrictMode.disableDeathOnFileUriExposure(); try { - safelyStartActivityInternal(cti, user); + safelyStartActivityInternal(cti, user, options); } finally { StrictMode.enableDeathOnFileUriExposure(); } } - - private void safelyStartActivityInternal(TargetInfo cti, UserHandle user) { + private void safelyStartActivityInternal( + TargetInfo cti, UserHandle user, @Nullable Bundle options) { // If the target is suspended, the activity will not be successfully launched. // Do not unregister from package manager updates in this case if (!cti.isSuspended() && mRegistered) { @@ -1356,14 +1361,14 @@ public class ResolverActivity extends Activity implements Toast.makeText(this, mProfileSwitchMessage, Toast.LENGTH_LONG).show(); } if (!mSafeForwardingMode) { - if (cti.startAsUser(this, null, user)) { + if (cti.startAsUser(this, options, user)) { onActivityStarted(cti); maybeLogCrossProfileTargetLaunch(cti, user); } return; } try { - if (cti.startAsCaller(this, null, user.getIdentifier())) { + if (cti.startAsCaller(this, options, user.getIdentifier())) { onActivityStarted(cti); maybeLogCrossProfileTargetLaunch(cti, user); } diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java index 2ed0f981692e..b32afb44852a 100644 --- a/core/java/com/android/internal/app/ResolverListAdapter.java +++ b/core/java/com/android/internal/app/ResolverListAdapter.java @@ -265,6 +265,7 @@ public class ResolverListAdapter extends BaseAdapter { return mResolverListController.getResolversForIntent( /* shouldGetResolvedFilter= */ true, mResolverListCommunicator.shouldGetActivityMetadata(), + mResolverListCommunicator.shouldGetOnlyDefaultActivities(), mIntents); } } @@ -416,8 +417,9 @@ public class ResolverListAdapter extends BaseAdapter { if (ii == null) { continue; } - ActivityInfo ai = ii.resolveActivityInfo( - mPm, 0); + // Because of AIDL bug, resolveActivityInfo can't accept subclasses of Intent. + final Intent rii = (ii.getClass() == Intent.class) ? ii : new Intent(ii); + ActivityInfo ai = rii.resolveActivityInfo(mPm, 0); if (ai == null) { Log.w(TAG, "No activity found for " + ii); continue; @@ -727,6 +729,7 @@ public class ResolverListAdapter extends BaseAdapter { protected List<ResolvedComponentInfo> getResolversForUser(UserHandle userHandle) { return mResolverListController.getResolversForIntentAsUser(true, mResolverListCommunicator.shouldGetActivityMetadata(), + mResolverListCommunicator.shouldGetOnlyDefaultActivities(), mIntents, userHandle); } @@ -820,6 +823,12 @@ public class ResolverListAdapter extends BaseAdapter { boolean shouldGetActivityMetadata(); + /** + * @return true to filter only apps that can handle + * {@link android.content.Intent#CATEGORY_DEFAULT} intents + */ + default boolean shouldGetOnlyDefaultActivities() { return true; }; + Intent getTargetIntent(); void onHandlePackagesChanged(ResolverListAdapter listAdapter); diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index 27573631b2ce..01dcf9624ed5 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -110,17 +110,19 @@ public class ResolverListController { public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntent( boolean shouldGetResolvedFilter, boolean shouldGetActivityMetadata, + boolean shouldGetOnlyDefaultActivities, List<Intent> intents) { return getResolversForIntentAsUser(shouldGetResolvedFilter, shouldGetActivityMetadata, - intents, mUserHandle); + shouldGetOnlyDefaultActivities, intents, mUserHandle); } public List<ResolverActivity.ResolvedComponentInfo> getResolversForIntentAsUser( boolean shouldGetResolvedFilter, boolean shouldGetActivityMetadata, + boolean shouldGetOnlyDefaultActivities, List<Intent> intents, UserHandle userHandle) { - int baseFlags = PackageManager.MATCH_DEFAULT_ONLY + int baseFlags = (shouldGetOnlyDefaultActivities ? PackageManager.MATCH_DEFAULT_ONLY : 0) | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | (shouldGetResolvedFilter ? PackageManager.GET_RESOLVED_FILTER : 0) @@ -134,12 +136,15 @@ public class ResolverListController { int baseFlags) { List<ResolverActivity.ResolvedComponentInfo> resolvedComponents = null; for (int i = 0, N = intents.size(); i < N; i++) { - final Intent intent = intents.get(i); + Intent intent = intents.get(i); int flags = baseFlags; if (intent.isWebIntent() || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) { flags |= PackageManager.MATCH_INSTANT; } + // Because of AIDL bug, queryIntentActivitiesAsUser can't accept subclasses of Intent. + intent = (intent.getClass() == Intent.class) ? intent : new Intent( + intent); final List<ResolveInfo> infos = mpm.queryIntentActivitiesAsUser(intent, flags, userHandle); if (infos != null) { diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java index 825b486d0635..dbfa4d35974b 100644 --- a/core/java/com/android/internal/jank/FrameTracker.java +++ b/core/java/com/android/internal/jank/FrameTracker.java @@ -26,10 +26,12 @@ import static android.view.SurfaceControl.JankData.SURFACE_FLINGER_SCHEDULING; import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL; import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_END; +import static com.android.internal.jank.InteractionJankMonitor.EXECUTOR_TASK_TIMEOUT; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UiThread; import android.graphics.HardwareRendererObserver; import android.os.Handler; import android.os.Trace; @@ -85,8 +87,9 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener public @interface Reasons { } + @VisibleForTesting + public final InteractionJankMonitor mMonitor; private final HardwareRendererObserver mObserver; - private SurfaceControl mSurfaceControl; private final int mTraceThresholdMissedFrames; private final int mTraceThresholdFrameTimeMillis; private final ThreadedRendererWrapper mRendererWrapper; @@ -99,17 +102,17 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener private final Handler mHandler; private final ChoreographerWrapper mChoreographer; private final StatsLogWrapper mStatsLog; - private final Object mLock = InteractionJankMonitor.getInstance().getLock(); private final boolean mDeferMonitoring; + private final FrameTrackerListener mListener; @VisibleForTesting public final boolean mSurfaceOnly; + private SurfaceControl mSurfaceControl; private long mBeginVsyncId = INVALID_ID; private long mEndVsyncId = INVALID_ID; private boolean mMetricsFinalized; private boolean mCancelled = false; - private FrameTrackerListener mListener; private boolean mTracingStarted = false; private Runnable mWaitForFinishTimedOut; @@ -142,16 +145,52 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener this.jankType = jankType; this.isFirstFrame = isFirstFrame; } + + @Override + public String toString() { + StringBuilder str = new StringBuilder(); + switch (jankType) { + case JANK_NONE: + str.append("JANK_NONE"); + break; + case JANK_APP_DEADLINE_MISSED: + str.append("JANK_APP_DEADLINE_MISSED"); + break; + case JANK_SURFACEFLINGER_DEADLINE_MISSED: + str.append("JANK_SURFACEFLINGER_DEADLINE_MISSED"); + break; + case JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED: + str.append("JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED"); + break; + case DISPLAY_HAL: + str.append("DISPLAY_HAL"); + break; + case PREDICTION_ERROR: + str.append("PREDICTION_ERROR"); + break; + case SURFACE_FLINGER_SCHEDULING: + str.append("SURFACE_FLINGER_SCHEDULING"); + break; + default: + str.append("UNKNOWN: ").append(jankType); + break; + } + str.append(", ").append(frameVsyncId); + str.append(", ").append(totalDurationNanos); + return str.toString(); + } } - public FrameTracker(@NonNull Session session, @NonNull Handler handler, - @Nullable ThreadedRendererWrapper renderer, @Nullable ViewRootWrapper viewRootWrapper, + public FrameTracker(@NonNull InteractionJankMonitor monitor, @NonNull Session session, + @NonNull Handler handler, @Nullable ThreadedRendererWrapper renderer, + @Nullable ViewRootWrapper viewRootWrapper, @NonNull SurfaceControlWrapper surfaceControlWrapper, @NonNull ChoreographerWrapper choreographer, @Nullable FrameMetricsWrapper metrics, @NonNull StatsLogWrapper statsLog, int traceThresholdMissedFrames, int traceThresholdFrameTimeMillis, @Nullable FrameTrackerListener listener, @NonNull Configuration config) { + mMonitor = monitor; mSurfaceOnly = config.isSurfaceOnly(); mSession = session; mHandler = handler; @@ -186,17 +225,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener mSurfaceChangedCallback = new ViewRootImpl.SurfaceChangedCallback() { @Override public void surfaceCreated(SurfaceControl.Transaction t) { - synchronized (mLock) { + getHandler().runWithScissors(() -> { if (mSurfaceControl == null) { mSurfaceControl = mViewRoot.getSurfaceControl(); if (mBeginVsyncId != INVALID_ID) { - mSurfaceControlWrapper.addJankStatsListener( - FrameTracker.this, mSurfaceControl); - markEvent("FT#deferMonitoring"); - postTraceStartMarker(); + // Previous begin invocation is not successfully, begin it again. + begin(); } } - } + }, EXECUTOR_TASK_TIMEOUT); } @Override @@ -208,18 +245,16 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener // Wait a while to give the system a chance for the remaining // frames to arrive, then force finish the session. - mHandler.postDelayed(() -> { - synchronized (mLock) { - if (DEBUG) { - Log.d(TAG, "surfaceDestroyed: " + mSession.getName() - + ", finalized=" + mMetricsFinalized - + ", info=" + mJankInfos.size() - + ", vsync=" + mBeginVsyncId); - } - if (!mMetricsFinalized) { - end(REASON_END_SURFACE_DESTROYED); - finish(); - } + getHandler().postDelayed(() -> { + if (DEBUG) { + Log.d(TAG, "surfaceDestroyed: " + mSession.getName() + + ", finalized=" + mMetricsFinalized + + ", info=" + mJankInfos.size() + + ", vsync=" + mBeginVsyncId); + } + if (!mMetricsFinalized) { + end(REASON_END_SURFACE_DESTROYED); + finish(); } }, 50); } @@ -230,35 +265,42 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } } + @VisibleForTesting + public Handler getHandler() { + return mHandler; + } + /** * Begin a trace session of the CUJ. */ + @UiThread public void begin() { - synchronized (mLock) { - final long currentVsync = mChoreographer.getVsyncId(); - // In normal case, we should begin at the next frame, - // the id of the next frame is not simply increased by 1, - // but we can exclude the current frame at least. + final long currentVsync = mChoreographer.getVsyncId(); + // In normal case, we should begin at the next frame, + // the id of the next frame is not simply increased by 1, + // but we can exclude the current frame at least. + if (mBeginVsyncId == INVALID_ID) { mBeginVsyncId = mDeferMonitoring ? currentVsync + 1 : currentVsync; + } + if (mSurfaceControl != null) { if (DEBUG) { Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId - + ", defer=" + mDeferMonitoring); + + ", defer=" + mDeferMonitoring + ", current=" + currentVsync); } - if (mSurfaceControl != null) { - if (mDeferMonitoring) { - markEvent("FT#deferMonitoring"); - // Normal case, we begin the instrument from the very beginning, - // will exclude the first frame. - postTraceStartMarker(); - } else { - // If we don't begin the instrument from the very beginning, - // there is no need to skip the frame where the begin invocation happens. - beginInternal(); - } - mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl); + if (mDeferMonitoring && currentVsync < mBeginVsyncId) { + markEvent("FT#deferMonitoring"); + // Normal case, we begin the instrument from the very beginning, + // will exclude the first frame. + postTraceStartMarker(this::beginInternal); + } else { + // If we don't begin the instrument from the very beginning, + // there is no need to skip the frame where the begin invocation happens. + beginInternal(); } - if (!mSurfaceOnly) { - mRendererWrapper.addObserver(mObserver); + } else { + if (DEBUG) { + Log.d(TAG, "begin: defer beginning since the surface is not ready for CUJ=" + + mSession.getName()); } } } @@ -267,89 +309,89 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener * Start trace section at appropriate time. */ @VisibleForTesting - public void postTraceStartMarker() { - mChoreographer.mChoreographer.postCallback( - Choreographer.CALLBACK_INPUT, this::beginInternal, null); + public void postTraceStartMarker(Runnable action) { + mChoreographer.mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, action, null); } + @UiThread private void beginInternal() { - synchronized (mLock) { - if (mCancelled || mEndVsyncId != INVALID_ID) { - return; - } - mTracingStarted = true; - markEvent("FT#begin"); - Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId); + if (mCancelled || mEndVsyncId != INVALID_ID) { + return; + } + mTracingStarted = true; + markEvent("FT#begin"); + Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId); + mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl); + if (!mSurfaceOnly) { + mRendererWrapper.addObserver(mObserver); } } /** * End the trace session of the CUJ. */ + @UiThread public boolean end(@Reasons int reason) { - synchronized (mLock) { - if (mCancelled || mEndVsyncId != INVALID_ID) return false; - mEndVsyncId = mChoreographer.getVsyncId(); - // Cancel the session if: - // 1. The session begins and ends at the same vsync id. - // 2. The session never begun. - if (mBeginVsyncId == INVALID_ID) { - return cancel(REASON_CANCEL_NOT_BEGUN); - } else if (mEndVsyncId <= mBeginVsyncId) { - return cancel(REASON_CANCEL_SAME_VSYNC); - } else { - if (DEBUG) { - Log.d(TAG, "end: " + mSession.getName() - + ", end=" + mEndVsyncId + ", reason=" + reason); - } - markEvent("FT#end#" + reason); - Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); - mSession.setReason(reason); - - // We don't remove observer here, - // will remove it when all the frame metrics in this duration are called back. - // See onFrameMetricsAvailable for the logic of removing the observer. - // Waiting at most 10 seconds for all callbacks to finish. - mWaitForFinishTimedOut = () -> { - Log.e(TAG, "force finish cuj because of time out:" + mSession.getName()); - finish(); - }; - mHandler.postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10)); - notifyCujEvent(ACTION_SESSION_END); - return true; + if (mCancelled || mEndVsyncId != INVALID_ID) return false; + mEndVsyncId = mChoreographer.getVsyncId(); + // Cancel the session if: + // 1. The session begins and ends at the same vsync id. + // 2. The session never begun. + if (mBeginVsyncId == INVALID_ID) { + return cancel(REASON_CANCEL_NOT_BEGUN); + } else if (mEndVsyncId <= mBeginVsyncId) { + return cancel(REASON_CANCEL_SAME_VSYNC); + } else { + if (DEBUG) { + Log.d(TAG, "end: " + mSession.getName() + + ", end=" + mEndVsyncId + ", reason=" + reason); } + markEvent("FT#end#" + reason); + Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); + mSession.setReason(reason); + + // We don't remove observer here, + // will remove it when all the frame metrics in this duration are called back. + // See onFrameMetricsAvailable for the logic of removing the observer. + // Waiting at most 10 seconds for all callbacks to finish. + mWaitForFinishTimedOut = () -> { + Log.e(TAG, "force finish cuj because of time out:" + mSession.getName()); + finish(); + }; + getHandler().postDelayed(mWaitForFinishTimedOut, TimeUnit.SECONDS.toMillis(10)); + notifyCujEvent(ACTION_SESSION_END); + return true; } } /** * Cancel the trace session of the CUJ. */ + @UiThread public boolean cancel(@Reasons int reason) { - synchronized (mLock) { - final boolean cancelFromEnd = - reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC; - if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false; - mCancelled = true; - markEvent("FT#cancel#" + reason); - // We don't need to end the trace section if it never begun. - if (mTracingStarted) { - Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); - } - - // Always remove the observers in cancel call to avoid leakage. - removeObservers(); + final boolean cancelFromEnd = + reason == REASON_CANCEL_NOT_BEGUN || reason == REASON_CANCEL_SAME_VSYNC; + if (mCancelled || (mEndVsyncId != INVALID_ID && !cancelFromEnd)) return false; + mCancelled = true; + markEvent("FT#cancel#" + reason); + // We don't need to end the trace section if it has never begun. + if (mTracingStarted) { + Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId); + } - if (DEBUG) { - Log.d(TAG, "cancel: " + mSession.getName() + ", begin=" + mBeginVsyncId - + ", end=" + mEndVsyncId + ", reason=" + reason); - } + // Always remove the observers in cancel call to avoid leakage. + removeObservers(); - mSession.setReason(reason); - // Notify the listener the session has been cancelled. - // We don't notify the listeners if the session never begun. - notifyCujEvent(ACTION_SESSION_CANCEL); - return true; + if (DEBUG) { + Log.d(TAG, "cancel: " + mSession.getName() + ", begin=" + mBeginVsyncId + + ", end=" + mEndVsyncId + ", reason=" + reason); } + + mSession.setReason(reason); + // Notify the listener the session has been cancelled. + // We don't notify the listeners if the session never begun. + notifyCujEvent(ACTION_SESSION_CANCEL); + return true; } private void markEvent(String desc) { @@ -364,8 +406,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener @Override public void onJankDataAvailable(SurfaceControl.JankData[] jankData) { - synchronized (mLock) { - if (mCancelled) { + postCallback(() -> { + if (mCancelled || mMetricsFinalized) { return; } @@ -384,10 +426,19 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener } } processJankInfos(); - } + }); } - private @Nullable JankInfo findJankInfo(long frameVsyncId) { + /** + * For easier argument capture. + */ + @VisibleForTesting + public void postCallback(Runnable callback) { + getHandler().post(callback); + } + + @Nullable + private JankInfo findJankInfo(long frameVsyncId) { return mJankInfos.get((int) frameVsyncId); } @@ -400,8 +451,8 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener @Override public void onFrameMetricsAvailable(int dropCountSinceLastInvocation) { - synchronized (mLock) { - if (mCancelled) { + postCallback(() -> { + if (mCancelled || mMetricsFinalized) { return; } @@ -426,9 +477,10 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener frameVsyncId, totalDurationNanos, isFirstFrame)); } processJankInfos(); - } + }); } + @UiThread private boolean hasReceivedCallbacksAfterEnd() { if (mEndVsyncId == INVALID_ID) { return false; @@ -451,6 +503,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener return false; } + @UiThread private void processJankInfos() { if (mMetricsFinalized) { return; @@ -467,9 +520,12 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener : info.hwuiCallbackFired && info.surfaceControlCallbackFired; } + @UiThread private void finish() { - mHandler.removeCallbacks(mWaitForFinishTimedOut); + getHandler().removeCallbacks(mWaitForFinishTimedOut); mWaitForFinishTimedOut = null; + if (mMetricsFinalized || mCancelled) return; + markEvent("FT#finish#" + mJankInfos.size()); mMetricsFinalized = true; // The tracing has been ended, remove the observer, see if need to trigger perfetto. @@ -496,7 +552,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener totalFramesCount++; boolean missedFrame = false; if ((info.jankType & JANK_APP_DEADLINE_MISSED) != 0) { - Log.w(TAG, "Missed App frame:" + info.jankType); + Log.w(TAG, "Missed App frame:" + info + ", CUJ=" + mSession.getName()); missedAppFramesCount++; missedFrame = true; } @@ -505,7 +561,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener || (info.jankType & JANK_SURFACEFLINGER_GPU_DEADLINE_MISSED) != 0 || (info.jankType & SURFACE_FLINGER_SCHEDULING) != 0 || (info.jankType & PREDICTION_ERROR) != 0) { - Log.w(TAG, "Missed SF frame:" + info.jankType); + Log.w(TAG, "Missed SF frame:" + info + ", CUJ=" + mSession.getName()); missedSfFramesCount++; missedFrame = true; } @@ -520,13 +576,15 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener // TODO (b/174755489): Early latch currently gets fired way too often, so we have // to ignore it for now. if (!mSurfaceOnly && !info.hwuiCallbackFired) { - Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId); + Log.w(TAG, "Missing HWUI jank callback for vsyncId: " + info.frameVsyncId + + ", CUJ=" + mSession.getName()); } } if (!mSurfaceOnly && info.hwuiCallbackFired) { maxFrameTimeNanos = Math.max(info.totalDurationNanos, maxFrameTimeNanos); if (!info.surfaceControlCallbackFired) { - Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId); + Log.w(TAG, "Missing SF jank callback for vsyncId: " + info.frameVsyncId + + ", CUJ=" + mSession.getName()); } } } @@ -586,6 +644,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener * Remove all the registered listeners, observers and callbacks. */ @VisibleForTesting + @UiThread public void removeObservers() { mSurfaceControlWrapper.removeJankStatsListener(this); if (!mSurfaceOnly) { @@ -601,7 +660,7 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener * Trigger the prefetto daemon. */ public void triggerPerfetto() { - InteractionJankMonitor.getInstance().trigger(mSession); + mMonitor.trigger(mSession); } /** @@ -666,10 +725,18 @@ public class FrameTracker extends SurfaceControl.OnJankDataListener mViewRoot = viewRoot; } + /** + * {@link ViewRootImpl#addSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback)} + * @param callback {@link ViewRootImpl.SurfaceChangedCallback} + */ public void addSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback) { mViewRoot.addSurfaceChangedCallback(callback); } + /** + * {@link ViewRootImpl#removeSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback)} + * @param callback {@link ViewRootImpl.SurfaceChangedCallback} + */ public void removeSurfaceChangedCallback(ViewRootImpl.SurfaceChangedCallback callback) { mViewRoot.removeSurfaceChangedCallback(callback); } diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 22340c6b0c55..8f10a5e3f049 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -53,6 +53,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_QS_TILE; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_DIALOG_OPEN; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_EXPAND_COLLAPSE_LOCK; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_HEADS_UP_APPEAR; @@ -85,8 +86,11 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.UiThread; +import android.annotation.WorkerThread; import android.content.Context; import android.os.Build; +import android.os.Handler; import android.os.HandlerExecutor; import android.os.HandlerThread; import android.provider.DeviceConfig; @@ -97,6 +101,7 @@ import android.view.Choreographer; import android.view.SurfaceControl; import android.view.View; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.FrameTracker.ChoreographerWrapper; import com.android.internal.jank.FrameTracker.FrameMetricsWrapper; @@ -130,6 +135,7 @@ public class InteractionJankMonitor { private static final String DEFAULT_WORKER_NAME = TAG + "-Worker"; private static final long DEFAULT_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(2L); + static final long EXECUTOR_TASK_TIMEOUT = 500; private static final String SETTINGS_ENABLED_KEY = "enabled"; private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval"; private static final String SETTINGS_THRESHOLD_MISSED_FRAMES_KEY = @@ -210,6 +216,7 @@ public class InteractionJankMonitor { public static final int CUJ_USER_DIALOG_OPEN = 59; public static final int CUJ_TASKBAR_EXPAND = 60; public static final int CUJ_TASKBAR_COLLAPSE = 61; + public static final int CUJ_SHADE_CLEAR_ALL = 62; private static final int NO_STATSD_LOGGING = -1; @@ -280,6 +287,7 @@ public class InteractionJankMonitor { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__USER_DIALOG_OPEN, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL, }; private static volatile InteractionJankMonitor sInstance; @@ -287,13 +295,14 @@ public class InteractionJankMonitor { private final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener = this::updateProperties; - private final FrameMetricsWrapper mMetrics; + @GuardedBy("mLock") private final SparseArray<FrameTracker> mRunningTrackers; + @GuardedBy("mLock") private final SparseArray<Runnable> mTimeoutActions; private final HandlerThread mWorker; private final Object mLock = new Object(); - private boolean mEnabled = DEFAULT_ENABLED; + private volatile boolean mEnabled = DEFAULT_ENABLED; private int mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; private int mTraceThresholdMissedFrames = DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES; private int mTraceThresholdFrameTimeMillis = DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS; @@ -361,7 +370,8 @@ public class InteractionJankMonitor { CUJ_SHADE_DIALOG_OPEN, CUJ_USER_DIALOG_OPEN, CUJ_TASKBAR_EXPAND, - CUJ_TASKBAR_COLLAPSE + CUJ_TASKBAR_COLLAPSE, + CUJ_SHADE_CLEAR_ALL }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -394,9 +404,7 @@ public class InteractionJankMonitor { mRunningTrackers = new SparseArray<>(); mTimeoutActions = new SparseArray<>(); mWorker = worker; - mMetrics = new FrameMetricsWrapper(); mWorker.start(); - mEnabled = DEFAULT_ENABLED; mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; // Post initialization to the background in case we're running on the main @@ -409,10 +417,7 @@ public class InteractionJankMonitor { DeviceConfig.NAMESPACE_INTERACTION_JANK_MONITOR, new HandlerExecutor(mWorker.getThreadHandler()), mPropertiesChangedListener); - } - - Object getLock() { - return mLock; + mEnabled = DEFAULT_ENABLED; } /** @@ -429,27 +434,27 @@ public class InteractionJankMonitor { view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer()); final ViewRootWrapper viewRoot = view == null ? null : new ViewRootWrapper(view.getViewRootImpl()); - final SurfaceControlWrapper surfaceControl = new SurfaceControlWrapper(); final ChoreographerWrapper choreographer = new ChoreographerWrapper(Choreographer.getInstance()); + final FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(act, s); + final FrameMetricsWrapper frameMetrics = new FrameMetricsWrapper(); - synchronized (mLock) { - FrameTrackerListener eventsListener = (s, act) -> handleCujEvents(act, s); - return new FrameTracker(session, mWorker.getThreadHandler(), - threadedRenderer, viewRoot, surfaceControl, choreographer, - mMetrics, new FrameTracker.StatsLogWrapper(), - mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, - eventsListener, config); - } + return new FrameTracker(this, session, config.getHandler(), threadedRenderer, viewRoot, + surfaceControl, choreographer, frameMetrics, new FrameTracker.StatsLogWrapper(), + mTraceThresholdMissedFrames, mTraceThresholdFrameTimeMillis, + eventsListener, config); } + @UiThread private void handleCujEvents(String action, Session session) { // Clear the running and timeout tasks if the end / cancel was fired within the tracker. // Or we might have memory leaks. if (needRemoveTasks(action, session)) { - removeTimeout(session.getCuj()); - removeTracker(session.getCuj()); + getTracker(session.getCuj()).getHandler().runWithScissors(() -> { + removeTimeout(session.getCuj()); + removeTracker(session.getCuj()); + }, EXECUTOR_TASK_TIMEOUT); } } @@ -466,7 +471,7 @@ public class InteractionJankMonitor { synchronized (mLock) { Runnable timeout = mTimeoutActions.get(cujType); if (timeout != null) { - mWorker.getThreadHandler().removeCallbacks(timeout); + getTracker(cujType).getHandler().removeCallbacks(timeout); mTimeoutActions.remove(cujType); } } @@ -491,9 +496,7 @@ public class InteractionJankMonitor { */ public boolean begin(View v, @CujType int cujType) { try { - return beginInternal( - Configuration.Builder.withView(cujType, v) - .build()); + return begin(Configuration.Builder.withView(cujType, v)); } catch (IllegalArgumentException ex) { Log.d(TAG, "Build configuration failed!", ex); return false; @@ -504,35 +507,42 @@ public class InteractionJankMonitor { * Begins a trace session. * * @param builder the builder of the configurations for instrumenting the CUJ. - * @return boolean true if the tracker is started successfully, false otherwise. + * @return boolean true if the tracker is begun successfully, false otherwise. */ public boolean begin(@NonNull Configuration.Builder builder) { try { - return beginInternal(builder.build()); + final Configuration config = builder.build(); + final TrackerResult result = new TrackerResult(); + final boolean success = config.getHandler().runWithScissors( + () -> result.mResult = beginInternal(config), EXECUTOR_TASK_TIMEOUT); + if (!success) { + Log.d(TAG, "begin failed due to timeout, CUJ=" + getNameOfCuj(config.mCujType)); + return false; + } + return result.mResult; } catch (IllegalArgumentException ex) { Log.d(TAG, "Build configuration failed!", ex); return false; } } + @UiThread private boolean beginInternal(@NonNull Configuration conf) { - synchronized (mLock) { - int cujType = conf.mCujType; - if (!shouldMonitor(cujType)) return false; - FrameTracker tracker = getTracker(cujType); - // Skip subsequent calls if we already have an ongoing tracing. - if (tracker != null) return false; - - // begin a new trace session. - tracker = createFrameTracker(conf, new Session(cujType, conf.mTag)); - mRunningTrackers.put(cujType, tracker); - tracker.begin(); - - // Cancel the trace if we don't get an end() call in specified duration. - scheduleTimeoutAction( - cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT)); - return true; - } + int cujType = conf.mCujType; + if (!shouldMonitor(cujType)) return false; + FrameTracker tracker = getTracker(cujType); + // Skip subsequent calls if we already have an ongoing tracing. + if (tracker != null) return false; + + // begin a new trace session. + tracker = createFrameTracker(conf, new Session(cujType, conf.mTag)); + putTracker(cujType, tracker); + tracker.begin(); + + // Cancel the trace if we don't get an end() call in specified duration. + scheduleTimeoutAction( + cujType, conf.mTimeout, () -> cancel(cujType, REASON_CANCEL_TIMEOUT)); + return true; } /** @@ -561,8 +571,10 @@ public class InteractionJankMonitor { */ @VisibleForTesting public void scheduleTimeoutAction(@CujType int cuj, long timeout, Runnable action) { - mTimeoutActions.put(cuj, action); - mWorker.getThreadHandler().postDelayed(action, timeout); + synchronized (mLock) { + mTimeoutActions.put(cuj, action); + getTracker(cuj).getHandler().postDelayed(action, timeout); + } } /** @@ -572,18 +584,35 @@ public class InteractionJankMonitor { * @return boolean true if the tracker is ended successfully, false otherwise. */ public boolean end(@CujType int cujType) { - synchronized (mLock) { - // remove the timeout action first. - removeTimeout(cujType); - FrameTracker tracker = getTracker(cujType); - // Skip this call since we haven't started a trace yet. - if (tracker == null) return false; - // if the end call doesn't return true, another thread is handling end of the cuj. - if (tracker.end(REASON_END_NORMAL)) { - removeTracker(cujType); + FrameTracker tracker = getTracker(cujType); + // Skip this call since we haven't started a trace yet. + if (tracker == null) return false; + try { + final TrackerResult result = new TrackerResult(); + final boolean success = tracker.getHandler().runWithScissors( + () -> result.mResult = endInternal(cujType), EXECUTOR_TASK_TIMEOUT); + if (!success) { + Log.d(TAG, "end failed due to timeout, CUJ=" + getNameOfCuj(cujType)); + return false; } - return true; + return result.mResult; + } catch (IllegalArgumentException ex) { + Log.d(TAG, "Execute end task failed!", ex); + return false; + } + } + + @UiThread + private boolean endInternal(@CujType int cujType) { + // remove the timeout action first. + removeTimeout(cujType); + FrameTracker tracker = getTracker(cujType); + if (tracker == null) return false; + // if the end call doesn't return true, another thread is handling end of the cuj. + if (tracker.end(REASON_END_NORMAL)) { + removeTracker(cujType); } + return true; } /** @@ -602,39 +631,66 @@ public class InteractionJankMonitor { */ @VisibleForTesting public boolean cancel(@CujType int cujType, @Reasons int reason) { - synchronized (mLock) { - // remove the timeout action first. - removeTimeout(cujType); - FrameTracker tracker = getTracker(cujType); - // Skip this call since we haven't started a trace yet. - if (tracker == null) return false; - // if the cancel call doesn't return true, another thread is handling cancel of the cuj. - if (tracker.cancel(reason)) { - removeTracker(cujType); + FrameTracker tracker = getTracker(cujType); + // Skip this call since we haven't started a trace yet. + if (tracker == null) return false; + try { + final TrackerResult result = new TrackerResult(); + final boolean success = tracker.getHandler().runWithScissors( + () -> result.mResult = cancelInternal(cujType, reason), EXECUTOR_TASK_TIMEOUT); + if (!success) { + Log.d(TAG, "cancel failed due to timeout, CUJ=" + getNameOfCuj(cujType)); + return false; } - return true; + return result.mResult; + } catch (IllegalArgumentException ex) { + Log.d(TAG, "Execute cancel task failed!", ex); + return false; + } + } + + @UiThread + private boolean cancelInternal(@CujType int cujType, @Reasons int reason) { + // remove the timeout action first. + removeTimeout(cujType); + FrameTracker tracker = getTracker(cujType); + if (tracker == null) return false; + // if the cancel call doesn't return true, another thread is handling cancel of the cuj. + if (tracker.cancel(reason)) { + removeTracker(cujType); + } + return true; + } + + private void putTracker(@CujType int cuj, @NonNull FrameTracker tracker) { + synchronized (mLock) { + mRunningTrackers.put(cuj, tracker); } } private FrameTracker getTracker(@CujType int cuj) { - return mRunningTrackers.get(cuj); + synchronized (mLock) { + return mRunningTrackers.get(cuj); + } } private void removeTracker(@CujType int cuj) { - mRunningTrackers.remove(cuj); + synchronized (mLock) { + mRunningTrackers.remove(cuj); + } } + @WorkerThread private void updateProperties(DeviceConfig.Properties properties) { - synchronized (mLock) { - mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, - DEFAULT_SAMPLING_INTERVAL); - mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); - mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY, - DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); - mTraceThresholdFrameTimeMillis = properties.getInt( - SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY, - DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); - } + mSamplingInterval = properties.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, + DEFAULT_SAMPLING_INTERVAL); + mTraceThresholdMissedFrames = properties.getInt(SETTINGS_THRESHOLD_MISSED_FRAMES_KEY, + DEFAULT_TRACE_THRESHOLD_MISSED_FRAMES); + mTraceThresholdFrameTimeMillis = properties.getInt( + SETTINGS_THRESHOLD_FRAME_TIME_MILLIS_KEY, + DEFAULT_TRACE_THRESHOLD_FRAME_TIME_MILLIS); + // The memory visibility is powered by the volatile field, mEnabled. + mEnabled = properties.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); } @VisibleForTesting @@ -804,10 +860,16 @@ public class InteractionJankMonitor { return "TASKBAR_EXPAND"; case CUJ_TASKBAR_COLLAPSE: return "TASKBAR_COLLAPSE"; + case CUJ_SHADE_CLEAR_ALL: + return "SHADE_CLEAR_ALL"; } return "UNKNOWN"; } + private static class TrackerResult { + private boolean mResult; + } + /** * Configurations used while instrumenting the CUJ. <br/> * <b>It may refer to an attached view, don't use static reference for any purpose.</b> @@ -821,6 +883,7 @@ public class InteractionJankMonitor { private final SurfaceControl mSurfaceControl; private final @CujType int mCujType; private final boolean mDeferMonitor; + private final Handler mHandler; /** * A builder for building Configuration. {@link #setView(View)} is essential @@ -964,6 +1027,7 @@ public class InteractionJankMonitor { mSurfaceControl = surfaceControl; mDeferMonitor = deferMonitor; validate(); + mHandler = mSurfaceOnly ? mContext.getMainThreadHandler() : mView.getHandler(); } private void validate() { @@ -1012,20 +1076,25 @@ public class InteractionJankMonitor { return mSurfaceControl; } - View getView() { + @VisibleForTesting + /** + * @return a view which is attached to the view tree. + */ + public View getView() { return mView; } - Context getContext() { - return mContext; - } - /** * @return true if the monitoring should be deferred to the next frame, false otherwise. */ public boolean shouldDeferMonitor() { return mDeferMonitor; } + + @VisibleForTesting + public Handler getHandler() { + return mHandler; + } } /** @@ -1078,7 +1147,8 @@ public class InteractionJankMonitor { mReason = reason; } - public @Reasons int getReason() { + @Reasons + public int getReason() { return mReason; } } diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 13bf64334f61..a352063aa6ee 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1440,8 +1440,9 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate, boolean force, WindowInsetsController controller) { state.present = state.attributes.isPresent( - controller.isRequestedVisible(state.attributes.insetsType), - mWindow.getAttributes().flags, force); + (controller.isRequestedVisible(state.attributes.insetsType) + || mLastShouldAlwaysConsumeSystemBars), + mWindow.getAttributes().flags, force); boolean show = state.attributes.isVisible(state.present, color, mWindow.getAttributes().flags, force); boolean showView = show && !isResizing() && !mHasCaption && size > 0; diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 1cdc10866f2c..af9c5a5cc0d5 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -53,7 +53,7 @@ public class ResolverDrawerLayout extends ViewGroup { /** * Max width of the whole drawer layout */ - private int mMaxWidth; + private final int mMaxWidth; /** * Max total visible height of views not marked always-show when in the closed/initial state @@ -187,8 +187,10 @@ public class ResolverDrawerLayout extends ViewGroup { } public void setSmallCollapsed(boolean smallCollapsed) { - mSmallCollapsed = smallCollapsed; - requestLayout(); + if (mSmallCollapsed != smallCollapsed) { + mSmallCollapsed = smallCollapsed; + requestLayout(); + } } public boolean isSmallCollapsed() { @@ -200,9 +202,10 @@ public class ResolverDrawerLayout extends ViewGroup { } public void setShowAtTop(boolean showOnTop) { - mShowAtTop = showOnTop; - invalidate(); - requestLayout(); + if (mShowAtTop != showOnTop) { + mShowAtTop = showOnTop; + requestLayout(); + } } public boolean getShowAtTop() { @@ -220,6 +223,9 @@ public class ResolverDrawerLayout extends ViewGroup { public void setCollapsibleHeightReserved(int heightPixels) { final int oldReserved = mCollapsibleHeightReserved; mCollapsibleHeightReserved = heightPixels; + if (oldReserved != mCollapsibleHeightReserved) { + requestLayout(); + } final int dReserved = mCollapsibleHeightReserved - oldReserved; if (dReserved != 0 && mIsDragging) { @@ -255,7 +261,7 @@ public class ResolverDrawerLayout extends ViewGroup { if (getShowAtTop()) { // Keep the drawer fully open. - mCollapseOffset = 0; + setCollapseOffset(0); return false; } @@ -264,9 +270,9 @@ public class ResolverDrawerLayout extends ViewGroup { if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight && mCollapseOffset == oldCollapsibleHeight)) { // Stay closed even at the new height. - mCollapseOffset = mCollapsibleHeight; + setCollapseOffset(mCollapsibleHeight); } else { - mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + setCollapseOffset(Math.min(mCollapseOffset, mCollapsibleHeight)); } final boolean isCollapsedNew = mCollapseOffset != 0; if (isCollapsedOld != isCollapsedNew) { @@ -274,11 +280,18 @@ public class ResolverDrawerLayout extends ViewGroup { } } else { // Start out collapsed at first unless we restored state for otherwise - mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; + setCollapseOffset(mOpenOnLayout ? 0 : mCollapsibleHeight); } return true; } + private void setCollapseOffset(float collapseOffset) { + if (mCollapseOffset != collapseOffset) { + mCollapseOffset = collapseOffset; + requestLayout(); + } + } + private int getMaxCollapsedHeight() { return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight) + mCollapsibleHeightReserved; @@ -420,8 +433,7 @@ public class ResolverDrawerLayout extends ViewGroup { case MotionEvent.ACTION_POINTER_DOWN: { final int pointerIndex = ev.getActionIndex(); - final int pointerId = ev.getPointerId(pointerIndex); - mActivePointerId = pointerId; + mActivePointerId = ev.getPointerId(pointerIndex); mInitialTouchX = ev.getX(pointerIndex); mInitialTouchY = mLastTouchY = ev.getY(pointerIndex); } @@ -924,7 +936,7 @@ public class ResolverDrawerLayout extends ViewGroup { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int sourceWidth = MeasureSpec.getSize(widthMeasureSpec); int widthSize = sourceWidth; - int heightSize = MeasureSpec.getSize(heightMeasureSpec); + final int heightSize = MeasureSpec.getSize(heightMeasureSpec); // Single-use layout; just ignore the mode and use available space. // Clamp to maxWidth. diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp index 4af28ea24361..1520ea5c6831 100644 --- a/core/jni/android_graphics_BLASTBufferQueue.cpp +++ b/core/jni/android_graphics_BLASTBufferQueue.cpp @@ -41,7 +41,12 @@ struct { static JNIEnv* getenv(JavaVM* vm) { JNIEnv* env; - if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + auto result = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); + if (result == JNI_EDETACHED) { + if (vm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!"); + } + } else if (result != JNI_OK) { LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); } return env; @@ -60,28 +65,22 @@ public: } ~TransactionHangCallbackWrapper() { - if (mTransactionHangObject) { - getenv()->DeleteGlobalRef(mTransactionHangObject); + if (mTransactionHangObject != nullptr) { + getenv(mVm)->DeleteGlobalRef(mTransactionHangObject); mTransactionHangObject = nullptr; } } void onTransactionHang(bool isGpuHang) { if (mTransactionHangObject) { - getenv()->CallVoidMethod(mTransactionHangObject, - gTransactionHangCallback.onTransactionHang, isGpuHang); + getenv(mVm)->CallVoidMethod(mTransactionHangObject, + gTransactionHangCallback.onTransactionHang, isGpuHang); } } private: JavaVM* mVm; jobject mTransactionHangObject; - - JNIEnv* getenv() { - JNIEnv* env; - mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6); - return env; - } }; static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring jName, diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index 92ac389ece67..505ef30949ce 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -578,7 +578,7 @@ message InsetsSourceProviderProto { optional WindowStateProto pending_control_target = 6; optional WindowStateProto fake_control_target = 7; optional .android.view.SurfaceControlProto captured_leash = 8; - optional .android.graphics.RectProto ime_overridden_frame = 9; + optional .android.graphics.RectProto ime_overridden_frame = 9 [deprecated=true]; optional bool is_leash_ready_for_dispatching = 10; optional bool client_visible = 11; optional bool server_visible = 12; diff --git a/core/res/res/drawable-nodpi/default_wallpaper.png b/core/res/res/drawable-nodpi/default_wallpaper.png Binary files differindex 5152972d2a80..a23f5539cca7 100644 --- a/core/res/res/drawable-nodpi/default_wallpaper.png +++ b/core/res/res/drawable-nodpi/default_wallpaper.png diff --git a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png Binary files differindex 26376fb87cbe..1e272e06221c 100644 --- a/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png +++ b/core/res/res/drawable-sw600dp-nodpi/default_wallpaper.png diff --git a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png Binary files differindex 490ebeeb75c1..d10c77d2f7f3 100644 --- a/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png +++ b/core/res/res/drawable-sw720dp-nodpi/default_wallpaper.png diff --git a/core/res/res/layout-car/car_alert_dialog.xml b/core/res/res/layout-car/car_alert_dialog.xml index 2e7b62ceb723..3c7a5c578fc5 100644 --- a/core/res/res/layout-car/car_alert_dialog.xml +++ b/core/res/res/layout-car/car_alert_dialog.xml @@ -54,7 +54,7 @@ android:layout_height="wrap_content" android:layout_marginStart="@dimen/text_view_start_margin" android:layout_marginEnd="@dimen/text_view_end_margin" - style="@style/CarBody4"/> + style="@style/CarDialogMessageText"/> <!-- we don't need this spacer, but the id needs to be here for compatibility --> <Space diff --git a/core/res/res/layout-car/car_alert_dialog_button_bar.xml b/core/res/res/layout-car/car_alert_dialog_button_bar.xml index 4f815b887088..43fd1eb4c25f 100644 --- a/core/res/res/layout-car/car_alert_dialog_button_bar.xml +++ b/core/res/res/layout-car/car_alert_dialog_button_bar.xml @@ -16,13 +16,13 @@ --> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/buttonPanel" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:scrollbarAlwaysDrawVerticalTrack="true" - android:scrollIndicators="top|bottom" - android:fillViewport="true" - style="?attr/buttonBarStyle"> + android:id="@+id/buttonPanel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:scrollbarAlwaysDrawVerticalTrack="true" + android:scrollIndicators="top|bottom" + android:fillViewport="true" + style="?attr/buttonBarStyle"> <com.android.internal.widget.ButtonBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" @@ -35,7 +35,7 @@ <Button android:id="@+id/button3" - style="@style/CarAction1" + style="@style/CarDialogButtonText" android:minWidth="@dimen/car_touch_target_size" android:paddingStart="@dimen/car_padding_2" android:paddingEnd="@dimen/car_padding_2" @@ -46,7 +46,7 @@ <Button android:id="@+id/button2" - style="@style/CarAction1" + style="@style/CarDialogButtonText" android:minWidth="@dimen/car_touch_target_size" android:paddingStart="@dimen/car_padding_2" android:paddingEnd="@dimen/car_padding_2" @@ -57,7 +57,7 @@ <Button android:id="@+id/button1" - style="@style/CarAction1" + style="@style/CarDialogButtonText" android:minWidth="@dimen/car_touch_target_size" android:paddingStart="@dimen/car_padding_2" android:paddingEnd="@dimen/car_padding_2" diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index b15d564fee71..a4d6bb4320f1 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -421,7 +421,7 @@ <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"መተግበሪያው በእርስዎ ስልክ ላይ ስለተከማቹ እውቂያዎችዎ ያለ ውሂብን እንዲቀይር ያስችለዋል። ይህ ፈቃድ መተግበሪያዎች የእውቂያ ውሂብን እንዲሰርዙ ያስችላቸዋል።"</string> <string name="permlab_readCallLog" msgid="1739990210293505948">"የጥሪ ምዝግብ ማስታወሻን ያንብቡ"</string> <string name="permdesc_readCallLog" msgid="8964770895425873433">"ይህ መተግበሪያ የእርስዎን የጥሪ ታሪክ ማንበብ ይችላል።"</string> - <string name="permlab_writeCallLog" msgid="670292975137658895">"የጥሪ ምዝግብ ማስታወሻን ፃፍ"</string> + <string name="permlab_writeCallLog" msgid="670292975137658895">"የጥሪ ምዝግብ ማስታወሻን ጻፍ"</string> <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የጡባዊተኮህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string> <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"መተግበሪያው ስለገቢ እና ወጪ ጥሪዎች ያለ ውሂብም ጨምሮ የእርስዎ Android TV መሣሪያ ምዝግብ ማስታወሻ እንዲቀይር ያስችለዋል። ተንኮል-አዘል መተግበሪያዎች ይህን ተጠቅመው የስልክዎን ምዝግብ ማስታወሻ ሊደመስሱ ወይም ሊቀይሩ ይችላሉ።"</string> <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"ስለ ገቢ እና ወጪ ጥሪዎችን ውሂብ ጨምሮ፣ የስልክህን ምዝግብ ማስታወሻ ለመቀየር ለመተግበሪያው ይፈቅዳል። ይሄንን ተንኮል አዘል መተግበሪያዎች የስልክህን ምዝግብ ማስታወሻ ለመሰረዝ ወይም ለመለወጥ ሊጠቀሙበት ይችላሉ።"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 9344e52841d3..4a6f8377f5ee 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1630,7 +1630,7 @@ <string name="display_manager_built_in_display_name" msgid="1015775198829722440">"Integrierter Bildschirm"</string> <string name="display_manager_hdmi_display_name" msgid="1022758026251534975">"HDMI-Bildschirm"</string> <string name="display_manager_overlay_display_name" msgid="5306088205181005861">"Overlay-Nr. <xliff:g id="ID">%1$d</xliff:g>"</string> - <string name="display_manager_overlay_display_title" msgid="1480158037150469170">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g> x <xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string> + <string name="display_manager_overlay_display_title" msgid="1480158037150469170">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g> × <xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string> <string name="display_manager_overlay_display_secure_suffix" msgid="2810034719482834679">", sicher"</string> <string name="kg_forgot_pattern_button_text" msgid="406145459223122537">"Muster vergessen"</string> <string name="kg_wrong_pattern" msgid="1342812634464179931">"Falsches Muster"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 409d69561356..f6b4ff4326c3 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -628,7 +628,7 @@ <string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Appuyez pour supprimer votre empreinte faciale, puis ajoutez de nouveau votre visage"</string> <string name="face_setup_notification_title" msgid="8843461561970741790">"Configurer le déverrouillage par reconnaissance faciale"</string> <string name="face_setup_notification_content" msgid="5463999831057751676">"Déverrouillez votre téléphone en le regardant"</string> - <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pour utiliser Face Unlock, activez "<b>"Accès à l\'appareil photo"</b>" dans Paramètres > Confidentialité"</string> + <string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pour utiliser le déverrouillage par reconnaissance faciale, activez "<b>"Accès à l\'appareil photo"</b>" dans Paramètres > Confidentialité"</string> <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurer d\'autres méthodes de déverrouillage"</string> <string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Appuyez pour ajouter une empreinte digitale"</string> <string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Déverrouillage par empreinte digitale"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 8066104d051d..37536fb9dacd 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -307,9 +307,9 @@ <string name="permgroupdesc_sms" msgid="5726462398070064542">"मैसेज (एसएमएस) भेजें और देखें"</string> <string name="permgrouplab_storage" msgid="17339216290379241">"फ़ाइलें"</string> <string name="permgroupdesc_storage" msgid="5378659041354582769">"अपने डिवाइस में मौजूद फ़ाइलों का ऐक्सेस दें"</string> - <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"संगीत और ऑडियो को ऐक्सेस करने की अनुमति"</string> + <string name="permgrouplab_readMediaAural" msgid="1858331312624942053">"संगीत और ऑडियो के ऐक्सेस"</string> <string name="permgroupdesc_readMediaAural" msgid="7565467343667089595">"आपके डिवाइस पर संगीत और ऑडियो को ऐक्सेस करने की अनुमति"</string> - <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"फ़ोटो और वीडियो को ऐक्सेस करने की अनुमति"</string> + <string name="permgrouplab_readMediaVisual" msgid="4724874717811908660">"फ़ोटो और वीडियो के ऐक्सेस"</string> <string name="permgroupdesc_readMediaVisual" msgid="4080463241903508688">"आपके डिवाइस पर फ़ोटो और वीडियो को ऐक्सेस करने की अनुमति"</string> <string name="permgrouplab_microphone" msgid="2480597427667420076">"माइक्रोफ़ोन"</string> <string name="permgroupdesc_microphone" msgid="1047786732792487722">"ऑडियो रिकॉर्ड करें"</string> @@ -555,7 +555,7 @@ <string name="permlab_disableKeyguard" msgid="3605253559020928505">"अपना स्क्रीन लॉक अक्षम करें"</string> <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"ऐप्स को कीलॉक और कोई भी संबद्ध पासवर्ड सुरक्षा बंद करने देता है. उदाहरण के लिए, इनकमिंग फ़ोन कॉल पाते समय फ़ोन, कीलॉक को बंद कर देता है, फिर कॉल खत्म होने पर कीलॉक को फिर से चालू कर देता है."</string> <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"जानें कि स्क्रीन लॉक कितना मुश्किल बनाया गया है"</string> - <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"यह मंज़ूरी मिलने के बाद ऐप्लिकेशन जान पाता है कि स्क्रीन लॉक कितना मुश्किल (बहुत ज़्यादा, मध्यम, कम या बिल्कुल नहीं) है. इस स्तर से यह पता चलता है कि स्क्रीन लॉक कितना लंबा या किस तरह का है. ऐप्लिकेशन उपयोगकर्ताओं को यह सुझाव भी दे सकता है कि वे स्क्रीन लॉक को एक तय लेवल तक अपडेट करें. लेकिन उपयोगकर्ता इसे बेझिझक अनदेखा करके छोड़ सकते हैं. ध्यान दें कि स्क्रीन लॉक को सादे टेक्स्ट में सेव नहीं किया जाता है इसलिए ऐप्लिकेशन को सटीक पासवर्ड पता नहीं होता है."</string> + <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"यह मंज़ूरी मिलने के बाद ऐप्लिकेशन जान पाता है कि स्क्रीन लॉक कितना मुश्किल (बहुत ज़्यादा, मध्यम, कम या बिलकुल नहीं) है. इस स्तर से यह पता चलता है कि स्क्रीन लॉक कितना लंबा या किस तरह का है. ऐप्लिकेशन उपयोगकर्ताओं को यह सुझाव भी दे सकता है कि वे स्क्रीन लॉक को एक तय लेवल तक अपडेट करें. लेकिन उपयोगकर्ता इसे बेझिझक अनदेखा करके छोड़ सकते हैं. ध्यान दें कि स्क्रीन लॉक को सादे टेक्स्ट में सेव नहीं किया जाता है इसलिए ऐप्लिकेशन को सटीक पासवर्ड पता नहीं होता है."</string> <string name="permlab_postNotification" msgid="4875401198597803658">"सूचनाएं दिखाएं"</string> <string name="permdesc_postNotification" msgid="5974977162462877075">"ऐप्लिकेशन को सूचनाएं दिखाने की अनुमति दें"</string> <string name="permlab_useBiometric" msgid="6314741124749633786">"बायोमीट्रिक हार्डवेयर इस्तेमाल करने दें"</string> @@ -649,9 +649,9 @@ <string name="face_acquired_recalibrate" msgid="8724013080976469746">"कृपया फिर से अपने चेहरे की पहचान कराएं."</string> <string name="face_acquired_too_different" msgid="2520389515612972889">"चेहरे की पहचान नहीं हुई. फिर से कोशिश करें."</string> <string name="face_acquired_too_similar" msgid="8882920552674125694">"अपने सिर की पोज़िशन को थोड़ा बदलें"</string> - <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"अपने फ़ोन की तरफ़ बिल्कुल सीधा देखें"</string> - <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"अपने फ़ोन की तरफ़ बिल्कुल सीधा देखें"</string> - <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"अपने फ़ोन की तरफ़ बिल्कुल सीधा देखें"</string> + <string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"अपने फ़ोन की तरफ़ बिलकुल सीधा देखें"</string> + <string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"अपने फ़ोन की तरफ़ बिलकुल सीधा देखें"</string> + <string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"अपने फ़ोन की तरफ़ बिलकुल सीधा देखें"</string> <string name="face_acquired_obscured" msgid="4917643294953326639">"आपके चेहरे को छिपाने वाली सभी चीज़ों को हटाएं."</string> <string name="face_acquired_sensor_dirty" msgid="8968391891086721678">"अपनी स्क्रीन के सबसे ऊपरी हिस्से को साफ़ करें, जिसमें काले रंग वाला बार भी शामिल है"</string> <string name="face_acquired_dark_glasses_detected" msgid="7263638432128692048">"आपका पूरा चेहरा दिखना चाहिए"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index b4c864daf532..7f767c012c28 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -750,7 +750,7 @@ <string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Mengizinkan pemegang memulai layar untuk meninjau keputusan izin. Tidak pernah dibutuhkan untuk aplikasi normal."</string> <string name="permlab_startViewAppFeatures" msgid="7955084203185903001">"mulai lihat fitur aplikasi"</string> <string name="permdesc_startViewAppFeatures" msgid="7207240860165206107">"Memungkinkan pemegang mulai melihat info fitur untuk aplikasi."</string> - <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"mengakses data sensor pada frekuensi pengambilan sampel yang tinggi"</string> + <string name="permlab_highSamplingRateSensors" msgid="3941068435726317070">"mengakses data sensor pada frekuensi sampling yang tinggi"</string> <string name="permdesc_highSamplingRateSensors" msgid="8430061978931155995">"Mengizinkan aplikasi mengambil sampel data sensor pada frekuensi yang lebih besar dari 200 Hz"</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"Setel aturan sandi"</string> <string name="policydesc_limitPassword" msgid="4105491021115793793">"Mengontrol panjang dan karakter yang diizinkan dalam sandi dan PIN kunci layar."</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 0431b50daaea..5241ddaceea6 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -1921,11 +1921,11 @@ <string name="user_creation_adding" msgid="7305185499667958364">"Consentire a <xliff:g id="APP">%1$s</xliff:g> di creare un nuovo utente con l\'account <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string> <string name="supervised_user_creation_label" msgid="6884904353827427515">"Aggiungi utente supervisionato"</string> <string name="language_selection_title" msgid="52674936078683285">"Aggiungi una lingua"</string> - <string name="country_selection_title" msgid="5221495687299014379">"Area geografica preferita"</string> + <string name="country_selection_title" msgid="5221495687299014379">"Regione preferita"</string> <string name="search_language_hint" msgid="7004225294308793583">"Digita nome lingua"</string> <string name="language_picker_section_suggested" msgid="6556199184638990447">"Suggerite"</string> <string name="language_picker_section_all" msgid="1985809075777564284">"Tutte le lingue"</string> - <string name="region_picker_section_all" msgid="756441309928774155">"Tutte le aree geografiche"</string> + <string name="region_picker_section_all" msgid="756441309928774155">"Tutte le regioni"</string> <string name="locale_search_menu" msgid="6258090710176422934">"Cerca"</string> <string name="app_suspended_title" msgid="888873445010322650">"App non disponibile"</string> <string name="app_suspended_default_message" msgid="6451215678552004172">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> non è al momento disponibile. Viene gestita tramite <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 8fb5b31988ee..74af0a4c45a9 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -1448,7 +1448,7 @@ <string name="permdesc_route_media_output" msgid="1759683269387729675">"メディア出力を他の外部デバイスにルーティングすることをアプリに許可します。"</string> <string name="permlab_readInstallSessions" msgid="7279049337895583621">"インストールセッションの読み取り"</string> <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"インストールセッションの読み取りをアプリに許可します。これにより、アプリはアクティブパッケージのインストールに関する詳細情報を参照できるようになります。"</string> - <string name="permlab_requestInstallPackages" msgid="7600020863445351154">"インストールパッケージのリクエスト"</string> + <string name="permlab_requestInstallPackages" msgid="7600020863445351154">"request install packages"</string> <string name="permdesc_requestInstallPackages" msgid="3969369278325313067">"パッケージのインストールをリクエストすることをアプリケーションに許可します。"</string> <string name="permlab_requestDeletePackages" msgid="2541172829260106795">"パッケージの削除のリクエスト"</string> <string name="permdesc_requestDeletePackages" msgid="6133633516423860381">"パッケージの削除をリクエストすることをアプリに許可します。"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 74bda7804c3e..15fc866023be 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -48,7 +48,7 @@ <string name="invalidPin" msgid="7542498253319440408">"4 ते 8 अंकांचा पिन टाइप करा."</string> <string name="invalidPuk" msgid="8831151490931907083">"8 अंकांचा किंवा मोठा PUK टाइप करा."</string> <string name="needPuk" msgid="7321876090152422918">"तुमचे सिम कार्ड PUK-लॉक केलेले आहे. ते अनलॉक करण्यासाठी PUK कोड टाइप करा."</string> - <string name="needPuk2" msgid="7032612093451537186">"सिम कार्ड अनावरोधित करण्यासाठी PUK2 टाइप करा."</string> + <string name="needPuk2" msgid="7032612093451537186">"सिम कार्ड अनब्लॉक करण्यासाठी PUK2 टाइप करा."</string> <string name="enablePin" msgid="2543771964137091212">"अयशस्वी, सिम/RUIM लॉक सुरू करा."</string> <plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584"> <item quantity="other">सिम लॉक होण्यापूर्वी आपल्याकडे <xliff:g id="NUMBER_1">%d</xliff:g> प्रयत्न उर्वरित आहेत.</item> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index d8c7f9e3d462..4b59bd84802a 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -1156,9 +1156,9 @@ <string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଚାଲୁଛି"</string> <string name="app_running_notification_text" msgid="5120815883400228566">"ଅଧିକ ସୂଚନା ପାଇଁ କିମ୍ବା ଆପ୍ ବନ୍ଦ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ।"</string> <string name="ok" msgid="2646370155170753815">"ଠିକ୍ ଅଛି"</string> - <string name="cancel" msgid="6908697720451760115">"ବାତିଲ୍ କରନ୍ତୁ"</string> + <string name="cancel" msgid="6908697720451760115">"ବାତିଲ କରନ୍ତୁ"</string> <string name="yes" msgid="9069828999585032361">"ଠିକ୍ ଅଛି"</string> - <string name="no" msgid="5122037903299899715">"ବାତିଲ୍ କରନ୍ତୁ"</string> + <string name="no" msgid="5122037903299899715">"ବାତିଲ କରନ୍ତୁ"</string> <string name="dialog_alert_title" msgid="651856561974090712">"ଧ୍ୟାନଦିଅନ୍ତୁ"</string> <string name="loading" msgid="3138021523725055037">"ଲୋଡ୍ କରାଯାଉଛି…"</string> <string name="capital_on" msgid="2770685323900821829">"ଚାଲୁ"</string> @@ -1179,9 +1179,9 @@ <string name="whichOpenLinksWithApp" msgid="6917864367861910086">"<xliff:g id="APPLICATION">%1$s</xliff:g> ମାଧ୍ୟମରେ ଲିଙ୍କ୍ଗୁଡ଼ିକ ଖୋଲନ୍ତୁ"</string> <string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"<xliff:g id="APPLICATION">%2$s</xliff:g> ମାଧ୍ୟମରେ <xliff:g id="HOST">%1$s</xliff:g> ଲିଙ୍କ୍ଗୁଡ଼ିକ ଖୋଲନ୍ତୁ"</string> <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"ଆକ୍ସେସ୍ ଦିଅନ୍ତୁ"</string> - <string name="whichEditApplication" msgid="6191568491456092812">"ସହିତ ଏଡିଟ୍ କରନ୍ତୁ"</string> - <string name="whichEditApplicationNamed" msgid="8096494987978521514">"%1$sରେ ସଂଶୋଧନ କରନ୍ତୁ"</string> - <string name="whichEditApplicationLabel" msgid="1463288652070140285">"ଏଡିଟ୍ କରନ୍ତୁ"</string> + <string name="whichEditApplication" msgid="6191568491456092812">"ସହିତ ଏଡିଟ କରନ୍ତୁ"</string> + <string name="whichEditApplicationNamed" msgid="8096494987978521514">"%1$sରେ ଏଡିଟ କରନ୍ତୁ"</string> + <string name="whichEditApplicationLabel" msgid="1463288652070140285">"ଏଡିଟ କରନ୍ତୁ"</string> <string name="whichSendApplication" msgid="4143847974460792029">"ସେୟାର୍ କରନ୍ତୁ"</string> <string name="whichSendApplicationNamed" msgid="4470386782693183461">"%1$s ସହିତ ସେୟାର୍ କରନ୍ତୁ"</string> <string name="whichSendApplicationLabel" msgid="7467813004769188515">"ସେୟାର୍ କରନ୍ତୁ"</string> @@ -1323,7 +1323,7 @@ <string name="sms_short_code_details" msgid="2723725738333388351">"ଏହା ଦ୍ୱାରା "<b>" ଆପଣଙ୍କ ମୋବାଇଲ୍ ଆକାଉଣ୍ଟରୁ ପଇସା କଟିପାରେ। "</b></string> <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>" ଆପଣଙ୍କ ମୋବାଇଲ୍ ଆକାଉଣ୍ଟରୁ ପଇସା କଟିପାରେ। "</b></string> <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"ପଠାନ୍ତୁ"</string> - <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"ବାତିଲ୍ କରନ୍ତୁ"</string> + <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"ବାତିଲ କରନ୍ତୁ"</string> <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"ମୋ ପସନ୍ଦ ମନେରଖନ୍ତୁ"</string> <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"ଏହାକୁ ଆପଣ ସେଟିଙ୍ଗ ଓ ଆପ୍ରେ ପରବର୍ତ୍ତୀ ସମୟରେ ବଦଳାଇପାରିବେ"</string> <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"ସର୍ବଦା ଅନୁମତି ଦିଅନ୍ତୁ"</string> @@ -1360,7 +1360,7 @@ <string name="usb_power_notification_message" msgid="7284765627437897702">"ଯୋଡ଼ାଯାଇଥିବା ଡିଭାଇସ୍ ଚାର୍ଜ ହେଉଛି। ଅଧିକ ବିକଳ୍ପ ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string> <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"ଆନାଲଗ୍ ଅଡିଓ ଆକ୍ସେସରୀ ଚିହ୍ନଟ ହେଲା"</string> <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"ଏହି ଫୋନ୍ରେ କନେକ୍ଟ ଥିବା ଡିଭାଇସ୍ କମ୍ପାଟିବଲ୍ ନୁହେଁ। ଅଧିକ ଜାଣିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string> - <string name="adb_active_notification_title" msgid="408390247354560331">"USB ଡିବଗିଂ ସଂଯୁକ୍ତ ହୋଇଛି"</string> + <string name="adb_active_notification_title" msgid="408390247354560331">"USB ଡିବଗିଂ କନେକ୍ଟ କରାଯାଇଛି"</string> <string name="adb_active_notification_message" msgid="5617264033476778211">"USB ଡିବଗିଂକୁ ବନ୍ଦ କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"USB ଡିବଗିଙ୍ଗକୁ ଅକ୍ଷମ କରିବା ପାଇଁ ଚୟନ କରନ୍ତୁ।"</string> <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"ୱାୟାରଲେସ୍ ଡିବଗିଂ ସଂଯୋଗ କରାଯାଇଛି"</string> @@ -1546,7 +1546,7 @@ <string name="date_picker_prev_month_button" msgid="3418694374017868369">"ପୂର୍ବ ମାସ"</string> <string name="date_picker_next_month_button" msgid="4858207337779144840">"ପରବର୍ତ୍ତୀ ମାସ"</string> <string name="keyboardview_keycode_alt" msgid="8997420058584292385">"ALT"</string> - <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"ବାତିଲ୍ କରନ୍ତୁ"</string> + <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"ବାତିଲ କରନ୍ତୁ"</string> <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"ଡିଲିଟ୍ କରନ୍ତୁ"</string> <string name="keyboardview_keycode_done" msgid="2524518019001653851">"ହୋଇଗଲା"</string> <string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"ମୋଡ୍ ପରିବର୍ତ୍ତନ"</string> @@ -1569,7 +1569,7 @@ <string name="storage_usb_drive" msgid="448030813201444573">"USB ଡ୍ରାଇଭ୍"</string> <string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ଡ୍ରାଇଭ୍"</string> <string name="storage_usb" msgid="2391213347883616886">"USB ଷ୍ଟୋରେଜ୍"</string> - <string name="extract_edit_menu_button" msgid="63954536535863040">"ଏଡିଟ୍ କରନ୍ତୁ"</string> + <string name="extract_edit_menu_button" msgid="63954536535863040">"ଏଡିଟ କରନ୍ତୁ"</string> <string name="data_usage_warning_title" msgid="9034893717078325845">"ଡାଟା ଚେତାବନୀ"</string> <string name="data_usage_warning_body" msgid="1669325367188029454">"ଆପଣ <xliff:g id="APP">%s</xliff:g> ଡାଟା ବ୍ୟବହାର କରିସାରିଛନ୍ତି"</string> <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"ମୋବାଇଲ୍ ଡାଟା ଧାର୍ଯ୍ୟ ସୀମାରେ ପହଞ୍ଚିଲା"</string> @@ -1695,7 +1695,7 @@ <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"ଆକ୍ସେସିବିଲିଟୀ ବଟନ୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଫିଚରଗୁଡ଼ିକ ବାଛନ୍ତୁ"</string> <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"ଭଲ୍ୟୁମ୍ କୀ ସର୍ଟକଟ୍ ସହିତ ବ୍ୟବହାର କରିବାକୁ ଫିଚରଗୁଡ଼ିକ ବାଛନ୍ତୁ"</string> <string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> ବନ୍ଦ ହୋଇଯାଇଛି"</string> - <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ସର୍ଟକଟଗୁଡ଼ିକୁ ସମ୍ପାଦନ କରନ୍ତୁ"</string> + <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"ସର୍ଟକଟଗୁଡ଼ିକୁ ଏଡିଟ କରନ୍ତୁ"</string> <string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"ହୋଇଗଲା"</string> <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"ଶର୍ଟକଟ୍ ବନ୍ଦ କରନ୍ତୁ"</string> <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"ଶର୍ଟକଟ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string> @@ -2048,7 +2048,7 @@ <string name="log_access_confirmation_body" msgid="6581985716241928135">"ଆପଣଙ୍କ ଡିଭାଇସରେ ଯାହା ହୁଏ ତାହା ଡିଭାଇସ ଲଗଗୁଡ଼ିକ ରେକର୍ଡ କରେ। ସମସ୍ୟାଗୁଡ଼ିକୁ ଖୋଜି ସମାଧାନ କରିବାକୁ ଆପ୍ସ ଏହି ଲଗଗୁଡ଼ିକୁ ବ୍ୟବହାର କରିପାରିବ।\n\nକିଛି ଲଗରେ ସମ୍ବେଦନଶୀଳ ସୂଚନା ଥାଇପାରେ, ତେଣୁ ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଆପଣ ବିଶ୍ୱାସ କରୁଥିବା ଆପ୍ସକୁ ହିଁ ଅନୁମତି ଦିଅନ୍ତୁ। \n\nଯଦି ଆପଣ ସମସ୍ତ ଡିଭାଇସ ଲଗକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ଏହି ଆପକୁ ଅନୁମତି ଦିଅନ୍ତି ନାହିଁ, ତେବେ ବି ଏହା ନିଜର ଡିଭାଇସ ଲଗଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିପାରିବ। ଆପଣଙ୍କ ଡିଭାଇସର ନିର୍ମାତା ଏବେ ବି ଆପଣଙ୍କର ଡିଭାଇସରେ କିଛି ଲଗ କିମ୍ବା ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ ସକ୍ଷମ ହୋଇପାରନ୍ତି। ଅଧିକ ଜାଣନ୍ତୁ"</string> <string name="log_access_do_not_show_again" msgid="1058690599083091552">"ପୁଣି ଦେଖାନ୍ତୁ ନାହିଁ"</string> <string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g>, <xliff:g id="APP_2">%2$s</xliff:g> ସ୍ଲାଇସ୍କୁ ଦେଖାଇବା ପାଇଁ ଚାହେଁ"</string> - <string name="screenshot_edit" msgid="7408934887203689207">"ଏଡିଟ୍ କରନ୍ତୁ"</string> + <string name="screenshot_edit" msgid="7408934887203689207">"ଏଡିଟ କରନ୍ତୁ"</string> <string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"କଲ୍ ଓ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକ ଭାଇବ୍ରେଟ୍ ହେବ"</string> <string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"କଲ୍ ଓ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ନିଃଶବ୍ଦ କରିଦିଆଯିବ"</string> <string name="notification_channel_system_changes" msgid="2462010596920209678">"ସିଷ୍ଟମ୍ରେ ପରିବର୍ତ୍ତନ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index cc3af05e200a..3635e69c2cac 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -1272,7 +1272,7 @@ <string name="volume_ringtone" msgid="134784084629229029">"ਰਿੰਗਰ ਵੌਲਿਊਮ"</string> <string name="volume_music" msgid="7727274216734955095">"ਮੀਡੀਆ ਦੀ ਅਵਾਜ਼"</string> <string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"Bluetooth ਰਾਹੀਂ ਪਲੇ ਕਰ ਰਿਹਾ ਹੈ"</string> - <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"ਖਾਮੋਸ਼ ਰਿੰਗਟੋਨ ਸੈੱਟ ਕੀਤੀ"</string> + <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"ਸ਼ਾਂਤ ਰਿੰਗਟੋਨ ਸੈੱਟ ਕੀਤੀ"</string> <string name="volume_call" msgid="7625321655265747433">"ਇਨ-ਕਾਲ ਅਵਾਜ਼"</string> <string name="volume_bluetooth_call" msgid="2930204618610115061">"ਬਲੂਟੁੱਥ ਇਨ-ਕਾਲ ਅਵਾਜ਼"</string> <string name="volume_alarm" msgid="4486241060751798448">"ਅਲਾਰਮ ਦੀ ਅਵਾਜ਼"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index ce8837b14011..0d2f038a49d7 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -2065,7 +2065,7 @@ <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Zavrieť"</string> <string name="notification_app_name_system" msgid="3045196791746735601">"Systém"</string> <string name="notification_app_name_settings" msgid="9088548800899952531">"Nastavenia"</string> - <string name="notification_appops_camera_active" msgid="8177643089272352083">"Fotoaparát"</string> + <string name="notification_appops_camera_active" msgid="8177643089272352083">"Kamera"</string> <string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofón"</string> <string name="notification_appops_overlay_active" msgid="5571732753262836481">"sa zobrazuje cez ďalšie aplikácie na obrazovke"</string> <string name="notification_feedback_indicator" msgid="663476517711323016">"Poskytnúť spätnú väzbu"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 40a8c6129038..77a6cdcf060f 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -2270,7 +2270,7 @@ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Återaktivera enhetens mikrofon"</string> <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Återaktivera enhetens kamera"</string> <string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"För <b><xliff:g id="APP">%s</xliff:g></b> och alla appar och tjänster"</string> - <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Återaktivera"</string> + <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Avblockera"</string> <string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Sensorintegritet"</string> <string name="splash_screen_view_icon_description" msgid="180638751260598187">"Appikon"</string> <string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Appens varumärkesbild"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 64789fb32dcb..721f76bc187c 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -1689,7 +1689,7 @@ <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"స్క్రీన్పై ఉండే కంటెంట్ మొత్తాన్ని చదవగలుగుతుంది మరియు ఇతర యాప్లలో కూడా ఈ కంటెంట్ను ప్రదర్శిస్తుంది."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"చర్యలను చూసి, అమలు చేయగలగడం"</string> <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"మీరు ఒక యాప్తో చేసే ఇంటరాక్షన్లను లేదా హార్డ్వేర్ సెన్సార్ను ట్రాక్ చేస్తూ మీ తరఫున యాప్లతో ఇంటరాక్ట్ చేయగలదు."</string> - <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"అనుమతించు"</string> + <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"అనుమతించండి"</string> <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"నిరాకరించు"</string> <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"ఫీచర్ని ఉపయోగించడం ప్రారంభించడానికి, దాన్ని ట్యాప్ చేయండి:"</string> <string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"యాక్సెసిబిలిటీ బటన్తో ఉపయోగించడానికి ఫీచర్లను ఎంచుకోండి"</string> @@ -1975,7 +1975,7 @@ <string name="tooltip_popup_title" msgid="7863719020269945722">"సాధనం చిట్కా"</string> <string name="app_category_game" msgid="4534216074910244790">"గేమ్లు"</string> <string name="app_category_audio" msgid="8296029904794676222">"సంగీతం & ఆడియో"</string> - <string name="app_category_video" msgid="2590183854839565814">"చలనచిత్రాలు & వీడియో"</string> + <string name="app_category_video" msgid="2590183854839565814">"సినిమాలు & వీడియో"</string> <string name="app_category_image" msgid="7307840291864213007">"ఫోటోలు, ఇమేజ్లు"</string> <string name="app_category_social" msgid="2278269325488344054">"సామాజికం & కమ్యూనికేషన్"</string> <string name="app_category_news" msgid="1172762719574964544">"వార్తలు & వార్తాపత్రికలు"</string> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index b754100a3ed6..2542268a153a 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -684,7 +684,7 @@ <!-- Lighting and shadow properties --> <dimen name="light_y">0dp</dimen> - <dimen name="light_z">600dp</dimen> + <dimen name="light_z">500dp</dimen> <dimen name="light_radius">800dp</dimen> <item type="dimen" format="float" name="ambient_shadow_alpha">0.039</item> <item type="dimen" format="float" name="spot_shadow_alpha">0.19</item> diff --git a/core/res/res/values/styles_car.xml b/core/res/res/values/styles_car.xml index ca3ba936630e..0655fde70934 100644 --- a/core/res/res/values/styles_car.xml +++ b/core/res/res/values/styles_car.xml @@ -14,96 +14,15 @@ limitations under the License. --> <resources> - <!-- Car text --> - <style name="CarBody1"> - <item name="textStyle">normal</item> - <item name="textSize">@dimen/car_body1_size</item> - <item name="textColor">@color/car_body1</item> - </style> - - <style name="CarBody1.Light"> - <item name="textColor">@color/car_body1_light</item> - </style> - - <style name="CarBody1.Dark"> - <item name="textColor">@color/car_body2_dark</item> - </style> - - <style name="CarBody2"> - <item name="textStyle">normal</item> - <item name="textSize">@dimen/car_body2_size</item> - <item name="textColor">@color/car_body2</item> - </style> - - <style name="CarBody2.Dark"> - <item name="textColor">@color/car_body2_dark</item> - </style> - <style name="CarBody2.Light"> - <item name="textColor">@color/car_body2_light</item> - </style> - - <style name="CarBody3"> - <item name="textStyle">normal</item> - <item name="textSize">@dimen/car_body3_size</item> - <item name="textColor">@color/car_body3</item> - </style> - - <!-- The smallest styling for body text. The color of this text changes based on the day/night - mode. --> - <style name="CarBody4"> + <!-- The Dialog message text style--> + <style name="CarDialogMessageText"> <item name="textStyle">normal</item> <item name="textSize">@dimen/car_body4_size</item> <item name="textColor">@color/car_body4</item> </style> - - <style name="CarAction1"> - <item name="textStyle">bold</item> - <item name="textSize">@dimen/car_action1_size</item> - <item name="textColor">@color/control_default_material</item> - </style> - - <style name="CarAction1.Dark"> - <item name="textColor">@color/car_highlight_dark</item> - </style> - <style name="CarAction1.Light"> - <item name="textColor">@color/car_highlight_light</item> - </style> - - <!-- The styling for title text. The color of this text changes based on day/night mode. --> - <style name="CarTitle" > - <item name="textStyle">bold</item> - <item name="textSize">@dimen/car_title2_size</item> - <item name="textColor">@color/car_title</item> - </style> - - <!-- Title text that is permanently a dark color. --> - <style name="CarTitle.Dark" > - <item name="textColor">@color/car_title_dark</item> - </style> - - <!-- Title text that is permanently a light color. --> - <style name="CarTitle.Light" > - <item name="textColor">@color/car_title_light</item> - </style> - - <!-- Action bar --> - <style name="ActionBarTitle" parent="@style/Widget.DeviceDefault.TextView"> - <item name="android:singleLine">true</item> - <item name="android:textAppearance">?attr/textAppearanceLarge</item> - </style> - - <style name="ActionBarButton" - parent="@style/Widget.DeviceDefault.Button.Borderless.Colored"> - <item name="android:textAppearance">@style/ActionBarButtonTextAppearance</item> - <!-- Button's internal horizontal padding --> - <item name="android:paddingStart">@*android:dimen/car_padding_3</item> - <item name="android:paddingEnd">@*android:dimen/car_padding_3</item> - <item name="android:drawablePadding">@*android:dimen/car_padding_2</item> - <item name="android:maxWidth">@*android:dimen/action_bar_button_max_width</item> - </style> - - <style name="ActionBarButtonTextAppearance" - parent="@style/TextAppearance.DeviceDefault.Widget.Button.Borderless.Colored"> - <item name="android:textAllCaps">false</item> + <!-- This style makes Dialog button text use medium font weight. --> + <style name="CarDialogButtonText"> + <item name="android:textAppearance">@style/TextAppearance.DeviceDefault.Widget.Button</item> + <item name="android:textColor">@color/control_default_material</item> </style> </resources> diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java index 47ce2d87e69f..cc4fbab1f190 100644 --- a/core/tests/coretests/src/android/widget/TextViewTest.java +++ b/core/tests/coretests/src/android/widget/TextViewTest.java @@ -304,6 +304,23 @@ public class TextViewTest { assertFalse(mTextView.isCursorVisible()); } + @Test(expected = NullPointerException.class) + @UiThreadTest + public void setTextCharArrayNullThrows() { + mTextView = new TextView(mActivity); + mTextView.setText((char[]) null, 0, 0); + } + + @Test + @UiThreadTest + public void setTextCharArrayValidAfterSetTextString() { + mTextView = new TextView(mActivity); + mTextView.setText(new char[] { 'h', 'i'}, 0, 2); + CharSequence charWrapper = mTextView.getText(); + mTextView.setText("null out char wrapper"); + assertEquals("hi", charWrapper.toString()); + } + private String createLongText() { int size = 600 * 1000; final StringBuilder builder = new StringBuilder(size); diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index f448cb3091e7..b5194f637395 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -66,6 +66,7 @@ public class WindowOnBackInvokedDispatcherTest { MockitoAnnotations.initMocks(this); mDispatcher = new WindowOnBackInvokedDispatcher(true /* applicationCallbackEnabled */); mDispatcher.attachToWindow(mWindowSession, mWindow); + mDispatcher.onWindowFocusChanged(true); } private void waitForIdle() { @@ -152,4 +153,31 @@ public class WindowOnBackInvokedDispatcherTest { waitForIdle(); verify(mCallback2).onBackStarted(); } + + @Test + public void skipBackInvokeWhenNoFocus() throws RemoteException { + ArgumentCaptor<OnBackInvokedCallbackInfo> captor = + ArgumentCaptor.forClass(OnBackInvokedCallbackInfo.class); + + mDispatcher.registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1); + + verify(mWindowSession, times(1)).setOnBackInvokedCallbackInfo( + Mockito.eq(mWindow), + captor.capture()); + + verify(mWindowSession).setOnBackInvokedCallbackInfo(Mockito.eq(mWindow), captor.capture()); + + // Should invoke back if it's still in focused. + captor.getValue().getCallback().onBackInvoked(); + waitForIdle(); + verify(mCallback1).onBackInvoked(); + + // In case the focus has lost during back gesture. + mDispatcher.onWindowFocusChanged(false); + + captor.getValue().getCallback().onBackInvoked(); + waitForIdle(); + verifyZeroInteractions(mCallback1); + } } diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java index 82148422aabf..e8c7ce0b312b 100644 --- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java @@ -266,6 +266,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); final IChooserWrapper activity = (IChooserWrapper) mActivityRule.launchActivity( @@ -289,6 +290,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, "chooser test")); @@ -309,6 +311,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -329,6 +332,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -352,6 +356,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -378,6 +383,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -403,6 +409,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -424,6 +431,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -478,6 +486,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -518,6 +527,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -552,6 +562,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(null); Intent sendIntent = createSendTextIntent(); @@ -587,6 +598,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -649,6 +661,7 @@ public class ChooserActivityTest { when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); when(ChooserActivityOverrideData.getInstance().resolverListController.getLastChosen()) .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0)); @@ -688,6 +701,7 @@ public class ChooserActivityTest { when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); final IChooserWrapper activity = (IChooserWrapper) @@ -720,6 +734,7 @@ public class ChooserActivityTest { when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); final ChooserActivity activity = @@ -747,6 +762,7 @@ public class ChooserActivityTest { when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); MetricsLogger mockLogger = ChooserActivityOverrideData.getInstance().metricsLogger; @@ -776,6 +792,7 @@ public class ChooserActivityTest { when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); final IChooserWrapper activity = (IChooserWrapper) @@ -846,6 +863,7 @@ public class ChooserActivityTest { when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); final IChooserWrapper activity = (IChooserWrapper) @@ -922,6 +940,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -958,6 +977,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -997,6 +1017,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -1091,6 +1112,7 @@ public class ChooserActivityTest { when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -1124,6 +1146,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -1157,6 +1180,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -1189,6 +1213,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); mActivityRule.launchActivity(Intent.createChooser(sendIntent, null)); @@ -1218,6 +1243,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -1250,6 +1276,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -1284,6 +1311,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); when( @@ -1323,6 +1351,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -1365,6 +1394,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -1409,6 +1439,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -1485,6 +1516,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -1566,6 +1598,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); // Create direct share target @@ -1638,6 +1671,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); // Create direct share target @@ -1745,6 +1779,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -2060,6 +2095,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -2142,6 +2178,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -2229,6 +2266,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -2298,6 +2336,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); @@ -2446,6 +2485,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendTextIntent(); @@ -2475,6 +2515,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendTextIntent(); @@ -2528,6 +2569,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent chooserIntent = createChooserIntent(createSendTextIntent(), @@ -2664,6 +2706,7 @@ public class ChooserActivityTest { when(ChooserActivityOverrideData.getInstance().resolverListController.getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); // Create caller target which is duplicate with one of app targets @@ -3057,6 +3100,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when( @@ -3066,6 +3110,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(new ArrayList<>(workResolvedComponentInfos)); when( @@ -3075,6 +3120,7 @@ public class ChooserActivityTest { .getResolversForIntentAsUser( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class), eq(UserHandle.SYSTEM))) .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); @@ -3131,6 +3177,7 @@ public class ChooserActivityTest { .getResolversForIntent( Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(resolvedComponentInfos); } diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java index 43fba529182e..92c05b0fe9fc 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java @@ -96,6 +96,7 @@ public class ResolverActivityTest { when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); @@ -127,6 +128,7 @@ public class ResolverActivityTest { when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); waitForIdle(); @@ -171,6 +173,7 @@ public class ResolverActivityTest { when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); waitForIdle(); @@ -203,6 +206,7 @@ public class ResolverActivityTest { when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); when(sOverrides.resolverListController.getLastChosen()) .thenReturn(resolvedComponentInfos.get(0).getResolveInfoAt(0)); @@ -273,6 +277,7 @@ public class ResolverActivityTest { when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent); @@ -317,6 +322,7 @@ public class ResolverActivityTest { when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); when(sOverrides.resolverListController.getLastChosen()) .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0)); @@ -807,6 +813,7 @@ public class ResolverActivityTest { createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendImageIntent(); @@ -831,6 +838,7 @@ public class ResolverActivityTest { createResolvedComponentsForTest(1); when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); Intent sendIntent = createSendImageIntent(); @@ -888,6 +896,7 @@ public class ResolverActivityTest { when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(resolvedComponentInfos); when(sOverrides.resolverListController.getLastChosen()) .thenReturn(resolvedComponentInfos.get(1).getResolveInfoAt(0)); @@ -965,13 +974,16 @@ public class ResolverActivityTest { List<ResolvedComponentInfo> workResolvedComponentInfos) { when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))) .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos); when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(), Mockito.anyBoolean(), + Mockito.anyBoolean(), Mockito.isA(List.class), eq(UserHandle.SYSTEM))) .thenReturn(new ArrayList<>(personalResolvedComponentInfos)); diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java index e16d44854516..42593f60094b 100644 --- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java @@ -20,11 +20,14 @@ import static junit.framework.Assert.assertEquals; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasSize; +import static org.mockito.ArgumentMatchers.intThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.usage.IUsageStatsManager; @@ -48,6 +51,7 @@ import com.android.internal.app.ResolverActivity.ResolvedComponentInfo; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.invocation.InvocationOnMock; @@ -78,6 +82,8 @@ public class ResolverListControllerTest { Configuration config = new Configuration(); config.locale = Locale.getDefault(); List<ResolveInfo> services = new ArrayList<>(); + mUsm = new UsageStatsManager(mMockContext, mMockService); + when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm); when(mMockPackageManager.queryIntentServices(any(), anyInt())).thenReturn(services); when(mMockResources.getConfiguration()).thenReturn(config); when(mMockContext.getResources()).thenReturn(mMockResources); @@ -112,8 +118,6 @@ public class ResolverListControllerTest { doAnswer(answer).when(mMockService).reportChooserSelection( anyString(), anyInt(), anyString(), any(), anyString()); when(mMockContext.getOpPackageName()).thenReturn(refererPackage); - mUsm = new UsageStatsManager(mMockContext, mMockService); - when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm); mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent, refererPackage, UserHandle.USER_CURRENT, /* userHandle */ UserHandle.SYSTEM); mController.sort(new ArrayList<ResolvedComponentInfo>()); @@ -129,8 +133,6 @@ public class ResolverListControllerTest { Intent sendIntent = createSendImageIntent(annotation); String refererPackage = "test_referer_package"; List<ResolvedComponentInfo> resolvedComponents = createResolvedComponentsForTest(10); - mUsm = new UsageStatsManager(mMockContext, mMockService); - when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm); mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent, refererPackage, UserHandle.USER_CURRENT, /* userHandle */ UserHandle.SYSTEM); List<ResolvedComponentInfo> topKList = new ArrayList<>(resolvedComponents); @@ -151,6 +153,102 @@ public class ResolverListControllerTest { sortList, topKList); } + @Test + public void getResolversForIntent_usesResultsFromPackageManager() { + mockStats(); + List<ResolveInfo> infos = new ArrayList<>(); + infos.add(ResolverDataProvider.createResolveInfo(0, UserHandle.USER_CURRENT)); + when(mMockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), + any(UserHandle.class))).thenReturn(infos); + mController = new ResolverListController(mMockContext, mMockPackageManager, + createSendImageIntent("test"), null, UserHandle.USER_CURRENT, + /* userHandle= */ UserHandle.SYSTEM); + List<Intent> intents = new ArrayList<>(); + intents.add(createActionMainIntent()); + + List<ResolvedComponentInfo> resolvers = mController + .getResolversForIntent( + /* shouldGetResolvedFilter= */ true, + /* shouldGetActivityMetadata= */ true, + /* shouldGetOnlyDefaultActivities= */ true, + intents); + + assertThat(resolvers, hasSize(1)); + assertThat(resolvers.get(0).getResolveInfoAt(0), is(infos.get(0))); + } + + @Test + public void getResolversForIntent_shouldGetOnlyDefaultActivitiesTrue_addsFlag() { + mockStats(); + List<ResolveInfo> infos = new ArrayList<>(); + infos.add(ResolverDataProvider.createResolveInfo(0, UserHandle.USER_CURRENT)); + when(mMockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), + any(UserHandle.class))).thenReturn(infos); + mController = new ResolverListController(mMockContext, mMockPackageManager, + createSendImageIntent("test"), null, UserHandle.USER_CURRENT, + /* userHandle= */ UserHandle.SYSTEM); + List<Intent> intents = new ArrayList<>(); + intents.add(createActionMainIntent()); + + mController + .getResolversForIntent( + /* shouldGetResolvedFilter= */ true, + /* shouldGetActivityMetadata= */ true, + /* shouldGetOnlyDefaultActivities= */ true, + intents); + + verify(mMockPackageManager).queryIntentActivitiesAsUser(any(), + containsFlag(PackageManager.MATCH_DEFAULT_ONLY), any()); + } + + @Test + public void getResolversForIntent_shouldGetOnlyDefaultActivitiesFalse_doesNotAddFlag() { + mockStats(); + List<ResolveInfo> infos = new ArrayList<>(); + infos.add(ResolverDataProvider.createResolveInfo(0, UserHandle.USER_CURRENT)); + when(mMockPackageManager.queryIntentActivitiesAsUser(any(), anyInt(), + any(UserHandle.class))).thenReturn(infos); + mController = new ResolverListController(mMockContext, mMockPackageManager, + createSendImageIntent("test"), null, UserHandle.USER_CURRENT, + /* userHandle= */ UserHandle.SYSTEM); + List<Intent> intents = new ArrayList<>(); + intents.add(createActionMainIntent()); + + mController + .getResolversForIntent( + /* shouldGetResolvedFilter= */ true, + /* shouldGetActivityMetadata= */ true, + /* shouldGetOnlyDefaultActivities= */ false, + intents); + + verify(mMockPackageManager).queryIntentActivitiesAsUser(any(), + doesNotContainFlag(PackageManager.MATCH_DEFAULT_ONLY), any()); + } + + private int containsFlag(int flag) { + return intThat(new FlagMatcher(flag, /* contains= */ true)); + } + + private int doesNotContainFlag(int flag) { + return intThat(new FlagMatcher(flag, /* contains= */ false)); + } + + public static class FlagMatcher implements ArgumentMatcher<Integer> { + + private final int mFlag; + private final boolean mContains; + + public FlagMatcher(int flag, boolean contains) { + mFlag = flag; + mContains = contains; + } + + @Override + public boolean matches(Integer integer) { + return ((integer & mFlag) != 0) == mContains; + } + } + private UsageStats initStats(String packageName, String action, String annotation, int count) { ArrayMap<String, ArrayMap<String, Integer>> chooserCounts = new ArrayMap<>(); @@ -174,6 +272,24 @@ public class ResolverListControllerTest { return sendIntent; } + private Intent createActionMainIntent() { + Intent sendIntent = new Intent(); + sendIntent.setAction(Intent.ACTION_MAIN); + sendIntent.addCategory(Intent.CATEGORY_LAUNCHER); + return sendIntent; + } + + private void mockStats() { + final List<UsageStats> slices = new ArrayList<>(); + ParceledListSlice<UsageStats> stats = new ParceledListSlice<>(slices); + try { + when(mMockService.queryUsageStats(anyInt(), anyLong(), anyLong(), anyString(), + anyInt())).thenReturn(stats); + } catch (RemoteException e) { + e.printStackTrace(); + } + } + private Integer getCount( UsageStatsManager usm, String packageName, String action, String annotation) { if (usm == null) { diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java index 09fc7ea6fffd..e068730e9bda 100644 --- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java @@ -36,6 +36,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.only; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -83,6 +84,7 @@ public class FrameTrackerTest { private StatsLogWrapper mStatsLog; private ArgumentCaptor<OnJankDataListener> mListenerCapture; private SurfaceControl mSurfaceControl; + private ArgumentCaptor<Runnable> mRunnableArgumentCaptor; @Before public void setup() { @@ -99,6 +101,8 @@ public class FrameTrackerTest { mSurfaceControl = new SurfaceControl.Builder().setName("Surface").build(); mViewRootWrapper = mock(ViewRootWrapper.class); when(mViewRootWrapper.getSurfaceControl()).thenReturn(mSurfaceControl); + doNothing().when(mViewRootWrapper).addSurfaceChangedCallback(any()); + doNothing().when(mViewRootWrapper).removeSurfaceChangedCallback(any()); mSurfaceControlWrapper = mock(SurfaceControlWrapper.class); mListenerCapture = ArgumentCaptor.forClass(OnJankDataListener.class); @@ -109,23 +113,29 @@ public class FrameTrackerTest { mChoreographer = mock(ChoreographerWrapper.class); mStatsLog = mock(StatsLogWrapper.class); + mRunnableArgumentCaptor = ArgumentCaptor.forClass(Runnable.class); } private FrameTracker spyFrameTracker(int cuj, String postfix, boolean surfaceOnly) { + InteractionJankMonitor monitor = mock(InteractionJankMonitor.class); Handler handler = mRule.getActivity().getMainThreadHandler(); Session session = new Session(cuj, postfix); Configuration config = mock(Configuration.class); when(config.isSurfaceOnly()).thenReturn(surfaceOnly); when(config.getSurfaceControl()).thenReturn(mSurfaceControl); when(config.shouldDeferMonitor()).thenReturn(true); + View view = mRule.getActivity().getWindow().getDecorView(); + Handler spyHandler = spy(new Handler(handler.getLooper())); + when(config.getView()).thenReturn(surfaceOnly ? null : view); + when(config.getHandler()).thenReturn(spyHandler); FrameTracker frameTracker = Mockito.spy( - new FrameTracker(session, handler, mRenderer, mViewRootWrapper, + new FrameTracker(monitor, session, spyHandler, mRenderer, mViewRootWrapper, mSurfaceControlWrapper, mChoreographer, mWrapper, mStatsLog, /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1, /* FrameTrackerListener= */ null, config)); doNothing().when(frameTracker).triggerPerfetto(); - doNothing().when(frameTracker).postTraceStartMarker(); + doNothing().when(frameTracker).postTraceStartMarker(mRunnableArgumentCaptor.capture()); return frameTracker; } @@ -140,6 +150,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mRenderer, only()).addObserver(any()); // send first frame with a long duration - should not be taken into account @@ -173,6 +184,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mRenderer, only()).addObserver(any()); // send first frame - not janky @@ -208,6 +220,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mRenderer, only()).addObserver(any()); // send first frame - janky @@ -243,6 +256,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mRenderer, only()).addObserver(any()); // send first frame - not janky @@ -278,6 +292,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mRenderer, only()).addObserver(any()); // send first frame - not janky @@ -319,6 +334,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mRenderer, only()).addObserver(any()); // send first frame - not janky @@ -332,7 +348,7 @@ public class FrameTrackerTest { tracker.end(FrameTracker.REASON_END_NORMAL); // Send incomplete callback for 102L - sendSfFrame(102L, JANK_NONE); + sendSfFrame(tracker, 102L, JANK_NONE); // Send janky but complete callbck fo 103L sendFrame(tracker, 50, JANK_APP_DEADLINE_MISSED, 103L); @@ -356,6 +372,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mRenderer).addObserver(any()); // First frame - not janky @@ -380,6 +397,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mRenderer, only()).addObserver(any()); // end the trace session @@ -403,6 +421,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mRenderer, only()).addObserver(any()); // end the trace session at the same vsync id, end vsync id will less than the begin one. @@ -444,6 +463,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mSurfaceControlWrapper).addJankStatsListener(any(), any()); // First frame - not janky @@ -479,6 +499,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mSurfaceControlWrapper).addJankStatsListener(any(), any()); // First frame - janky @@ -514,6 +535,7 @@ public class FrameTrackerTest { when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mSurfaceControlWrapper).addJankStatsListener(any(), any()); // First frame - not janky @@ -548,6 +570,7 @@ public class FrameTrackerTest { CUJ_WALLPAPER_TRANSITION, CUJ_POSTFIX, /* surfaceOnly= */ true); when(mChoreographer.getVsyncId()).thenReturn(100L); tracker.begin(); + mRunnableArgumentCaptor.getValue().run(); verify(mSurfaceControlWrapper).addJankStatsListener(any(), any()); sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 100L); sendFrame(tracker, JANK_SURFACEFLINGER_DEADLINE_MISSED, 101L); @@ -594,7 +617,7 @@ public class FrameTrackerTest { if (!tracker.mSurfaceOnly) { sendHwuiFrame(tracker, durationMillis, vsyncId, firstWindowFrame); } - sendSfFrame(vsyncId, jankType); + sendSfFrame(tracker, vsyncId, jankType); } private void sendHwuiFrame(FrameTracker tracker, long durationMillis, long vsyncId, @@ -604,12 +627,18 @@ public class FrameTrackerTest { .getMetric(FrameMetrics.FIRST_DRAW_FRAME); doReturn(TimeUnit.MILLISECONDS.toNanos(durationMillis)) .when(mWrapper).getMetric(FrameMetrics.TOTAL_DURATION); + final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); + doNothing().when(tracker).postCallback(captor.capture()); tracker.onFrameMetricsAvailable(0); + captor.getValue().run(); } - private void sendSfFrame(long vsyncId, @JankType int jankType) { + private void sendSfFrame(FrameTracker tracker, long vsyncId, @JankType int jankType) { + final ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); + doNothing().when(tracker).postCallback(captor.capture()); mListenerCapture.getValue().onJankDataAvailable(new JankData[] { new JankData(vsyncId, jankType) }); + captor.getValue().run(); } } diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java index 5a6fd5317bbc..d96f041c13f8 100644 --- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java +++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java @@ -93,7 +93,7 @@ public class InteractionJankMonitorTest { @Test public void testBeginEnd() { InteractionJankMonitor monitor = createMockedInteractionJankMonitor(); - FrameTracker tracker = createMockedFrameTracker(null); + FrameTracker tracker = createMockedFrameTracker(monitor, null); doReturn(tracker).when(monitor).createFrameTracker(any(), any()); doNothing().when(tracker).begin(); doReturn(true).when(tracker).end(anyInt()); @@ -134,7 +134,7 @@ public class InteractionJankMonitorTest { public void testBeginTimeout() { ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); InteractionJankMonitor monitor = createMockedInteractionJankMonitor(); - FrameTracker tracker = createMockedFrameTracker(null); + FrameTracker tracker = createMockedFrameTracker(monitor, null); doReturn(tracker).when(monitor).createFrameTracker(any(), any()); doNothing().when(tracker).begin(); doReturn(true).when(tracker).cancel(anyInt()); @@ -180,7 +180,8 @@ public class InteractionJankMonitorTest { return monitor; } - private FrameTracker createMockedFrameTracker(FrameTracker.FrameTrackerListener listener) { + private FrameTracker createMockedFrameTracker(InteractionJankMonitor monitor, + FrameTracker.FrameTrackerListener listener) { Session session = spy(new Session(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE, CUJ_POSTFIX)); doReturn(false).when(session).logToStatsd(); @@ -190,6 +191,7 @@ public class InteractionJankMonitorTest { ViewRootWrapper viewRoot = spy(new ViewRootWrapper(mView.getViewRootImpl())); doNothing().when(viewRoot).addSurfaceChangedCallback(any()); + doNothing().when(viewRoot).removeSurfaceChangedCallback(any()); SurfaceControlWrapper surfaceControl = mock(SurfaceControlWrapper.class); doNothing().when(surfaceControl).addJankStatsListener(any(), any()); @@ -200,15 +202,18 @@ public class InteractionJankMonitorTest { Configuration configuration = mock(Configuration.class); when(configuration.isSurfaceOnly()).thenReturn(false); + when(configuration.getView()).thenReturn(mView); + when(configuration.getHandler()).thenReturn(mView.getHandler()); - FrameTracker tracker = spy(new FrameTracker(session, mWorker.getThreadHandler(), + FrameTracker tracker = spy(new FrameTracker(monitor, session, mWorker.getThreadHandler(), threadedRenderer, viewRoot, surfaceControl, choreographer, new FrameMetricsWrapper(), new StatsLogWrapper(), /* traceThresholdMissedFrames= */ 1, /* traceThresholdFrameTimeMillis= */ -1, listener, configuration)); - doNothing().when(tracker).postTraceStartMarker(); + doNothing().when(tracker).postTraceStartMarker(any()); doNothing().when(tracker).triggerPerfetto(); + doReturn(configuration.getHandler()).when(tracker).getHandler(); return tracker; } diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java index 8d3751e6ad6c..47f70ddf2d42 100644 --- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java +++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java @@ -207,8 +207,8 @@ public class ActivityThreadClientTest { final Configuration currentConfig = new Configuration(); assertFalse("Must not report change if no public diff", - shouldReportChange(0 /* publicDiff */, currentConfig, newConfig, - null /* sizeBuckets */, 0 /* handledConfigChanges */)); + shouldReportChange(currentConfig, newConfig, null /* sizeBuckets */, + 0 /* handledConfigChanges */)); final int[] verticalThresholds = {100, 400}; final SizeConfigurationBuckets buckets = new SizeConfigurationBuckets( @@ -221,25 +221,25 @@ public class ActivityThreadClientTest { newConfig.screenHeightDp = 300; assertFalse("Must not report changes if the diff is small and not handled", - shouldReportChange(CONFIG_SCREEN_SIZE /* publicDiff */, currentConfig, - newConfig, buckets, CONFIG_FONT_SCALE /* handledConfigChanges */)); + shouldReportChange(currentConfig, newConfig, buckets, + CONFIG_FONT_SCALE /* handledConfigChanges */)); assertTrue("Must report changes if the small diff is handled", - shouldReportChange(CONFIG_SCREEN_SIZE /* publicDiff */, currentConfig, newConfig, - buckets, CONFIG_SCREEN_SIZE /* handledConfigChanges */)); + shouldReportChange(currentConfig, newConfig, buckets, + CONFIG_SCREEN_SIZE /* handledConfigChanges */)); currentConfig.fontScale = 0.8f; newConfig.fontScale = 1.2f; assertTrue("Must report handled changes regardless of small unhandled change", - shouldReportChange(CONFIG_SCREEN_SIZE | CONFIG_FONT_SCALE /* publicDiff */, - currentConfig, newConfig, buckets, CONFIG_FONT_SCALE /* handledConfigChanges */)); + shouldReportChange(currentConfig, newConfig, buckets, + CONFIG_FONT_SCALE /* handledConfigChanges */)); newConfig.screenHeightDp = 500; assertFalse("Must not report changes if there's unhandled big changes", - shouldReportChange(CONFIG_SCREEN_SIZE | CONFIG_FONT_SCALE /* publicDiff */, - currentConfig, newConfig, buckets, CONFIG_FONT_SCALE /* handledConfigChanges */)); + shouldReportChange(currentConfig, newConfig, buckets, + CONFIG_FONT_SCALE /* handledConfigChanges */)); } private void recreateAndVerifyNoRelaunch(ActivityThread activityThread, TestActivity activity) { diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 6706e4e87e0d..f01e2e809c21 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -577,12 +577,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-1521427940": { - "message": "commitVisibility: %s: visible=%b mVisibleRequested=%b", - "level": "VERBOSE", - "group": "WM_DEBUG_APP_TRANSITIONS", - "at": "com\/android\/server\/wm\/ActivityRecord.java" - }, "-1517908912": { "message": "requestScrollCapture: caught exception dispatching to window.token=%s", "level": "WARN", @@ -1513,6 +1507,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/DisplayContent.java" }, + "-636553602": { + "message": "commitVisibility: %s: visible=%b visibleRequested=%b, isInTransition=%b, runningAnimation=%b, caller=%s", + "level": "VERBOSE", + "group": "WM_DEBUG_APP_TRANSITIONS", + "at": "com\/android\/server\/wm\/ActivityRecord.java" + }, "-635082269": { "message": "******** booted=%b msg=%b haveBoot=%b haveApp=%b haveWall=%b wallEnabled=%b haveKeyguard=%b", "level": "INFO", diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 84e949a18c52..f8c015f28a50 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -932,7 +932,7 @@ <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font> </family> <family lang="und-Laoo" variant="compact"> - <font weight="400" style="normal">NotoSansLaoUI-Regular.ttf + <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf </font> <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font> </family> 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 2f79caeec7ba..da9fd0c2d96f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -16,6 +16,7 @@ package androidx.window.extensions.embedding; +import static android.app.ActivityManager.START_SUCCESS; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -97,6 +98,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>(); private final Handler mHandler; private final Object mLock = new Object(); + private final ActivityStartMonitor mActivityStartMonitor; public SplitController() { final MainThreadExecutor executor = new MainThreadExecutor(); @@ -108,7 +110,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen new LifecycleCallbacks()); // Intercept activity starts to route activities to new containers if necessary. Instrumentation instrumentation = activityThread.getInstrumentation(); - instrumentation.addMonitor(new ActivityStartMonitor()); + mActivityStartMonitor = new ActivityStartMonitor(); + instrumentation.addMonitor(mActivityStartMonitor); } /** Updates the embedding rules applied to future activity launches. */ @@ -1385,6 +1388,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return ActivityThread.currentActivityThread().getActivity(activityToken); } + @VisibleForTesting + ActivityStartMonitor getActivityStartMonitor() { + return mActivityStartMonitor; + } + /** * Gets the token of the initial TaskFragment that embedded this activity. Do not rely on it * after creation because the activity could be reparented. @@ -1536,7 +1544,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen * A monitor that intercepts all activity start requests originating in the client process and * can amend them to target a specific task fragment to form a split. */ - private class ActivityStartMonitor extends Instrumentation.ActivityMonitor { + @VisibleForTesting + class ActivityStartMonitor extends Instrumentation.ActivityMonitor { + @VisibleForTesting + Intent mCurrentIntent; @Override public Instrumentation.ActivityResult onStartActivity(@NonNull Context who, @@ -1564,11 +1575,29 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // the dedicated container. options.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN, launchedInTaskFragment.getTaskFragmentToken()); + mCurrentIntent = intent; } } return super.onStartActivity(who, intent, options); } + + @Override + public void onStartActivityResult(int result, @NonNull Bundle bOptions) { + super.onStartActivityResult(result, bOptions); + if (mCurrentIntent != null && result != START_SUCCESS) { + // Clear the pending appeared intent if the activity was not started successfully. + final IBinder token = bOptions.getBinder( + ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN); + if (token != null) { + final TaskFragmentContainer container = getContainer(token); + if (container != null) { + container.clearPendingAppearedIntentIfNeeded(mCurrentIntent); + } + } + } + mCurrentIntent = null; + } } /** 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 abf32a26efa2..a188e2bf4985 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -198,6 +198,22 @@ class TaskFragmentContainer { return mPendingAppearedIntent; } + void setPendingAppearedIntent(@Nullable Intent intent) { + mPendingAppearedIntent = intent; + } + + /** + * Clears the pending appeared Intent if it is the same as given Intent. Otherwise, the + * pending appeared Intent is cleared when TaskFragmentInfo is set and is not empty (has + * running activities). + */ + void clearPendingAppearedIntentIfNeeded(@NonNull Intent intent) { + if (mPendingAppearedIntent == null || mPendingAppearedIntent != intent) { + return; + } + mPendingAppearedIntent = null; + } + boolean hasActivity(@NonNull IBinder token) { if (mInfo != null && mInfo.getActivities().contains(token)) { return true; @@ -230,13 +246,18 @@ class TaskFragmentContainer { void setInfo(@NonNull TaskFragmentInfo info) { if (!mIsFinished && mInfo == null && info.isEmpty()) { - // onTaskFragmentAppeared with empty info. We will remove the TaskFragment if it is - // still empty after timeout. + // onTaskFragmentAppeared with empty info. We will remove the TaskFragment if no + // pending appeared intent/activities. Otherwise, wait and removing the TaskFragment if + // it is still empty after timeout. mAppearEmptyTimeout = () -> { mAppearEmptyTimeout = null; mController.onTaskFragmentAppearEmptyTimeout(this); }; - mController.getHandler().postDelayed(mAppearEmptyTimeout, APPEAR_EMPTY_TIMEOUT_MS); + if (mPendingAppearedIntent != null || !mPendingAppearedActivities.isEmpty()) { + mController.getHandler().postDelayed(mAppearEmptyTimeout, APPEAR_EMPTY_TIMEOUT_MS); + } else { + mAppearEmptyTimeout.run(); + } } else if (mAppearEmptyTimeout != null && !info.isEmpty()) { mController.getHandler().removeCallbacks(mAppearEmptyTimeout); mAppearEmptyTimeout = null; 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 18086f552ea3..6bfb16a3c22d 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -25,8 +25,7 @@ import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; import android.annotation.Nullable; import android.app.Activity; -import android.app.ActivityManager; -import android.app.ActivityManager.AppTask; +import android.app.ActivityClient; import android.app.Application; import android.app.WindowConfiguration; import android.content.Context; @@ -34,7 +33,6 @@ import android.graphics.Rect; import android.os.Bundle; import android.os.IBinder; import android.util.ArrayMap; -import android.util.Log; import androidx.annotation.NonNull; import androidx.window.common.CommonFoldingFeature; @@ -179,57 +177,49 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { 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"); + if (!shouldReportDisplayFeatures(activity)) { return features; - } else if (isTaskInMultiWindowMode(activity)) { - // 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; - } else { - for (CommonFoldingFeature baseFeature : storedFeatures) { - Integer state = convertToExtensionState(baseFeature.getState()); - if (state == null) { - continue; - } - Rect featureRect = baseFeature.getRect(); - rotateRectToDisplayRotation(displayId, featureRect); - transformToWindowSpaceRect(activity, featureRect); + } - if (!isRectZero(featureRect)) { - // TODO(b/228641877) Remove guarding if when fixed. - features.add(new FoldingFeature(featureRect, baseFeature.getType(), state)); - } + int displayId = activity.getDisplay().getDisplayId(); + for (CommonFoldingFeature baseFeature : storedFeatures) { + Integer state = convertToExtensionState(baseFeature.getState()); + if (state == null) { + continue; + } + Rect featureRect = baseFeature.getRect(); + rotateRectToDisplayRotation(displayId, featureRect); + transformToWindowSpaceRect(activity, featureRect); + + if (!isRectZero(featureRect)) { + // TODO(b/228641877): Remove guarding when fixed. + features.add(new FoldingFeature(featureRect, baseFeature.getType(), state)); } - return features; } + return features; } /** - * Checks whether the task associated with the activity is in multi-window. If task info is not - * available it defaults to {@code true}. + * Checks whether display features should be reported for the activity. + * TODO(b/238948678): Support reporting display features in all windowing modes. */ - private boolean isTaskInMultiWindowMode(@NonNull Activity activity) { - final ActivityManager am = activity.getSystemService(ActivityManager.class); - if (am == null) { - return true; - } - - final List<AppTask> appTasks = am.getAppTasks(); - final int taskId = activity.getTaskId(); - AppTask task = null; - for (AppTask t : appTasks) { - if (t.getTaskInfo().taskId == taskId) { - task = t; - break; - } + private boolean shouldReportDisplayFeatures(@NonNull Activity activity) { + int displayId = activity.getDisplay().getDisplayId(); + if (displayId != DEFAULT_DISPLAY) { + // Display features are not supported on secondary displays. + return false; } - if (task == null) { - // The task might be removed on the server already. - return true; + final int taskWindowingMode = ActivityClient.getInstance().getTaskWindowingMode( + activity.getActivityToken()); + if (taskWindowingMode == -1) { + // If we cannot determine the task windowing mode for any reason, it is likely that we + // won't be able to determine its position correctly as well. DisplayFeatures' bounds + // in this case can't be computed correctly, so we should skip. + return false; } - return WindowConfiguration.inMultiWindowMode(task.getTaskInfo().getWindowingMode()); + // 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 !WindowConfiguration.inMultiWindowMode(taskWindowingMode); } /** 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 042547fd30f2..4bc503369d0e 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 @@ -16,6 +16,7 @@ package androidx.window.extensions.embedding; +import static android.app.ActivityManager.START_CANCELED; import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -293,6 +294,26 @@ public class SplitControllerTest { } @Test + public void testOnStartActivityResultError() { + final Intent intent = new Intent(); + final TaskContainer taskContainer = new TaskContainer(TASK_ID); + final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */, + intent, taskContainer, mSplitController); + final SplitController.ActivityStartMonitor monitor = + mSplitController.getActivityStartMonitor(); + + container.setPendingAppearedIntent(intent); + final Bundle bundle = new Bundle(); + bundle.putBinder(ActivityOptions.KEY_LAUNCH_TASK_FRAGMENT_TOKEN, + container.getTaskFragmentToken()); + monitor.mCurrentIntent = intent; + doReturn(container).when(mSplitController).getContainer(any()); + + monitor.onStartActivityResult(START_CANCELED, bundle); + assertNull(container.getPendingAppearedIntent()); + } + + @Test public void testOnActivityCreated() { mSplitController.onActivityCreated(mActivity); 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 28c2773e25cb..44c7e6c611de 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 @@ -209,21 +209,21 @@ public class TaskFragmentContainerTest { assertNull(container.mAppearEmptyTimeout); - // Not set if it is not appeared empty. - final TaskFragmentInfo info = mock(TaskFragmentInfo.class); - doReturn(new ArrayList<>()).when(info).getActivities(); - doReturn(false).when(info).isEmpty(); - container.setInfo(info); - - assertNull(container.mAppearEmptyTimeout); - // Set timeout if the first info set is empty. + final TaskFragmentInfo info = mock(TaskFragmentInfo.class); container.mInfo = null; doReturn(true).when(info).isEmpty(); container.setInfo(info); assertNotNull(container.mAppearEmptyTimeout); + // Not set if it is not appeared empty. + doReturn(new ArrayList<>()).when(info).getActivities(); + doReturn(false).when(info).isEmpty(); + container.setInfo(info); + + assertNull(container.mAppearEmptyTimeout); + // Remove timeout after the container becomes non-empty. doReturn(false).when(info).isEmpty(); container.setInfo(info); @@ -232,6 +232,7 @@ public class TaskFragmentContainerTest { // Running the timeout will call into SplitController.onTaskFragmentAppearEmptyTimeout. container.mInfo = null; + container.setPendingAppearedIntent(mIntent); doReturn(true).when(info).isEmpty(); container.setInfo(info); container.mAppearEmptyTimeout.run(); diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index 0f2d38bfeb65..87ac5d64ff85 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -72,8 +72,7 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"बबल"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"मैनेज करें"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"बबल खारिज किया गया."</string> - <!-- no translation found for restart_button_description (6712141648865547958) --> - <skip /> + <string name="restart_button_description" msgid="6712141648865547958">"टैप करके ऐप्लिकेशन को रीस्टार्ट करें और बेहतर व्यू पाएं."</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्या कैमरे से जुड़ी कोई समस्या है?\nफिर से फ़िट करने के लिए टैप करें"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index b4bc3909c08f..8fb7adebe930 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -72,7 +72,7 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"氣泡"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"管理"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"對話氣泡已關閉。"</string> - <string name="restart_button_description" msgid="6712141648865547958">"請輕觸並重新啟動此應用程式,取得更良好的觀看體驗。"</string> + <string name="restart_button_description" msgid="6712141648865547958">"輕按並重新啟動此應用程式,以取得更佳的觀看體驗。"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題?\n輕按即可修正"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java index 14ba9df93f24..764e650a807c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootDisplayAreaOrganizer.java @@ -85,6 +85,8 @@ public class RootDisplayAreaOrganizer extends DisplayAreaOrganizer { } mDisplayAreasInfo.remove(displayId); + mLeashes.get(displayId).release(); + mLeashes.remove(displayId); } @Override 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 992f31562145..6694e441084b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellInitImpl.java @@ -17,7 +17,16 @@ package com.android.wm.shell; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_INIT; +import android.os.Build; +import android.os.SystemClock; +import android.util.Pair; + +import androidx.annotation.VisibleForTesting; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.bubbles.BubbleController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -37,10 +46,12 @@ import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.UnfoldAnimationController; import com.android.wm.shell.unfold.UnfoldTransitionHandler; +import java.util.ArrayList; import java.util.Optional; /** - * The entry point implementation into the shell for initializing shell internal state. + * The entry point implementation into the shell for initializing shell internal state. Classes + * which need to setup on start should inject an instance of this class and add an init callback. */ public class ShellInitImpl { private static final String TAG = ShellInitImpl.class.getSimpleName(); @@ -62,8 +73,12 @@ public class ShellInitImpl { private final Transitions mTransitions; private final StartingWindowController mStartingWindow; private final Optional<RecentTasksController> mRecentTasks; + private final Optional<ActivityEmbeddingController> mActivityEmbeddingOptional; private final InitImpl mImpl = new InitImpl(); + // An ordered list of init callbacks to be made once shell is first started + private final ArrayList<Pair<String, Runnable>> mInitCallbacks = new ArrayList<>(); + private boolean mHasInitialized; public ShellInitImpl( DisplayController displayController, @@ -80,6 +95,7 @@ public class ShellInitImpl { Optional<UnfoldTransitionHandler> unfoldTransitionHandler, Optional<FreeformTaskListener<?>> freeformTaskListenerOptional, Optional<RecentTasksController> recentTasks, + Optional<ActivityEmbeddingController> activityEmbeddingOptional, Transitions transitions, StartingWindowController startingWindow, ShellExecutor mainExecutor) { @@ -97,6 +113,7 @@ public class ShellInitImpl { mUnfoldTransitionHandler = unfoldTransitionHandler; mFreeformTaskListenerOptional = freeformTaskListenerOptional; mRecentTasks = recentTasks; + mActivityEmbeddingOptional = activityEmbeddingOptional; mTransitions = transitions; mMainExecutor = mainExecutor; mStartingWindow = startingWindow; @@ -106,7 +123,7 @@ public class ShellInitImpl { return mImpl; } - private void init() { + private void legacyInit() { // Start listening for display and insets changes mDisplayController.initialize(); mDisplayInsetsController.initialize(); @@ -126,6 +143,7 @@ public class ShellInitImpl { if (Transitions.ENABLE_SHELL_TRANSITIONS) { mTransitions.register(mShellTaskOrganizer); + mActivityEmbeddingOptional.ifPresent(ActivityEmbeddingController::init); mUnfoldTransitionHandler.ifPresent(UnfoldTransitionHandler::init); if (mSplitScreenOptional.isPresent() && mPipTouchHandlerOptional.isPresent()) { final DefaultMixedHandler mixedHandler = new DefaultMixedHandler(mTransitions, @@ -153,12 +171,52 @@ public class ShellInitImpl { mKidsModeTaskOrganizer.initialize(mStartingWindow); } + /** + * Adds a callback to the ordered list of callbacks be made when Shell is first started. This + * can be used in class constructors when dagger is used to ensure that the initialization order + * matches the dependency order. + */ + public <T extends Object> void addInitCallback(Runnable r, T instance) { + if (mHasInitialized) { + if (Build.isDebuggable()) { + // All callbacks must be added prior to the Shell being initialized + throw new IllegalArgumentException("Can not add callback after init"); + } + return; + } + final String className = instance.getClass().getSimpleName(); + mInitCallbacks.add(new Pair<>(className, r)); + ProtoLog.v(WM_SHELL_INIT, "Adding init callback for %s", className); + } + + /** + * Calls all the init callbacks when the Shell is first starting. + */ + @VisibleForTesting + public void init() { + ProtoLog.v(WM_SHELL_INIT, "Initializing Shell Components: %d", mInitCallbacks.size()); + // Init in order of registration + for (int i = 0; i < mInitCallbacks.size(); i++) { + final Pair<String, Runnable> info = mInitCallbacks.get(i); + final long t1 = SystemClock.uptimeMillis(); + info.second.run(); + final long t2 = SystemClock.uptimeMillis(); + ProtoLog.v(WM_SHELL_INIT, "\t%s took %dms", info.first, (t2 - t1)); + } + mInitCallbacks.clear(); + + // TODO: To be removed + legacyInit(); + + mHasInitialized = true; + } + @ExternalThread private class InitImpl implements ShellInit { @Override public void init() { try { - mMainExecutor.executeBlocking(() -> ShellInitImpl.this.init()); + mMainExecutor.executeBlocking(ShellInitImpl.this::init); } catch (InterruptedException e) { throw new RuntimeException("Failed to initialize the Shell in 2s", e); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index e9d24fbf4d0a..7d7c59eb17da 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; +import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import android.annotation.IntDef; import android.annotation.NonNull; @@ -499,7 +500,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements || (taskInfo.topActivityType == WindowConfiguration.ACTIVITY_TYPE_HOME && taskInfo.isVisible); final boolean focusTaskChanged = (mLastFocusedTaskInfo == null - || mLastFocusedTaskInfo.taskId != taskInfo.taskId) && isFocusedOrHome; + || mLastFocusedTaskInfo.taskId != taskInfo.taskId + || mLastFocusedTaskInfo.getWindowingMode() != taskInfo.getWindowingMode()) + && isFocusedOrHome; if (focusTaskChanged) { for (int i = 0; i < mFocusListeners.size(); i++) { mFocusListeners.valueAt(i).onFocusTaskChanged(taskInfo); @@ -529,7 +532,8 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } final int taskId = taskInfo.taskId; - final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo()); + final TaskAppearedInfo appearedInfo = mTasks.get(taskId); + final TaskListener listener = getTaskListener(appearedInfo.getTaskInfo()); mTasks.remove(taskId); if (listener != null) { listener.onTaskVanished(taskInfo); @@ -539,6 +543,11 @@ public class ShellTaskOrganizer extends TaskOrganizer implements notifyCompatUI(taskInfo, null /* taskListener */); // Notify the recent tasks that a task has been removed mRecentTasks.ifPresent(recentTasks -> recentTasks.onTaskRemoved(taskInfo)); + + if (!ENABLE_SHELL_TRANSITIONS && (appearedInfo.getLeash() != null)) { + // Preemptively clean up the leash only if shell transitions are not enabled + appearedInfo.getLeash().release(); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java new file mode 100644 index 000000000000..82b0270698e4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java @@ -0,0 +1,80 @@ +/* + * 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.activityembedding; + +import static android.window.TransitionInfo.FLAG_IS_EMBEDDED; + +import android.content.Context; +import android.os.IBinder; +import android.view.SurfaceControl; +import android.window.TransitionInfo; +import android.window.TransitionRequestInfo; +import android.window.WindowContainerTransaction; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.wm.shell.transition.Transitions; + +/** + * Responsible for handling ActivityEmbedding related transitions. + */ +public class ActivityEmbeddingController implements Transitions.TransitionHandler { + + private final Context mContext; + private final Transitions mTransitions; + + public ActivityEmbeddingController(Context context, Transitions transitions) { + mContext = context; + mTransitions = transitions; + } + + /** Registers to handle transitions. */ + public void init() { + mTransitions.addHandler(this); + } + + @Override + public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { + // TODO(b/207070762) Handle AE animation as a part of other transitions. + // Only handle the transition if all containers are embedded. + for (TransitionInfo.Change change : info.getChanges()) { + if (!isEmbedded(change)) { + return false; + } + } + + // TODO(b/207070762) Implement AE animation. + startTransaction.apply(); + finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */); + return true; + } + + @Nullable + @Override + public WindowContainerTransaction handleRequest(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + return null; + } + + private static boolean isEmbedded(@NonNull TransitionInfo.Change change) { + return (change.getFlags() & FLAG_IS_EMBEDDED) != 0; + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt index b483fe03e80f..312af4ff7bc2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt @@ -829,8 +829,12 @@ class PhysicsAnimator<T> private constructor (target: T) { /** Cancels all in progress animations on all properties. */ fun cancel() { - cancelAction(flingAnimations.keys) - cancelAction(springAnimations.keys) + if (flingAnimations.size > 0) { + cancelAction(flingAnimations.keys) + } + if (springAnimations.size > 0) { + cancelAction(springAnimations.keys) + } } /** Cancels in progress animations on the provided properties only. */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java index 8c0affb0a432..86f9d5b534f4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java @@ -29,6 +29,13 @@ import com.android.wm.shell.common.annotations.ExternalThread; public interface BackAnimation { /** + * Returns a binder that can be passed to an external process to update back animations. + */ + default IBackAnimation createExternalInterface() { + return null; + } + + /** * Called when a {@link MotionEvent} is generated by a back gesture. * * @param touchX the X touch position of the {@link MotionEvent}. @@ -47,13 +54,6 @@ public interface BackAnimation { void setTriggerBack(boolean triggerBack); /** - * Returns a binder that can be passed to an external process to update back animations. - */ - default IBackAnimation createExternalInterface() { - return null; - } - - /** * Sets the threshold values that defining edge swipe behavior. * @param triggerThreshold the min threshold to trigger back. * @param progressThreshold the max threshold to keep progressing back animation. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 0cf2b28921e1..d53a98cf5b07 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -403,7 +403,9 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont private boolean shouldDispatchToLauncher(int backType) { return backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && mBackToLauncherCallback != null - && mEnableAnimations.get(); + && mEnableAnimations.get() + && mBackNavigationInfo != null + && mBackNavigationInfo.getDepartingAnimationTarget() != null; } private static void dispatchOnBackStarted(IOnBackInvokedCallback callback) { 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 a2c40550b583..d7f1292cb717 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 @@ -85,7 +85,6 @@ import androidx.annotation.MainThread; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.logging.UiEventLogger; import com.android.internal.statusbar.IStatusBarService; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TaskViewTransitions; @@ -102,6 +101,8 @@ import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.PinnedStackListenerForwarder; +import com.android.wm.shell.sysui.ConfigurationChangeListener; +import com.android.wm.shell.sysui.ShellController; import java.io.PrintWriter; import java.util.ArrayList; @@ -120,7 +121,7 @@ import java.util.function.IntConsumer; * * The controller manages addition, removal, and visible state of bubbles on screen. */ -public class BubbleController { +public class BubbleController implements ConfigurationChangeListener { private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES; @@ -156,6 +157,7 @@ public class BubbleController { private final DisplayController mDisplayController; private final TaskViewTransitions mTaskViewTransitions; private final SyncTransactionQueue mSyncQueue; + private final ShellController mShellController; // Used to post to main UI thread private final ShellExecutor mMainExecutor; @@ -223,44 +225,9 @@ public class BubbleController { /** Drag and drop controller to register listener for onDragStarted. */ private DragAndDropController mDragAndDropController; - /** - * Creates an instance of the BubbleController. - */ - public static BubbleController create(Context context, - @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, - FloatingContentCoordinator floatingContentCoordinator, - @Nullable IStatusBarService statusBarService, - WindowManager windowManager, - WindowManagerShellWrapper windowManagerShellWrapper, - UserManager userManager, - LauncherApps launcherApps, - TaskStackListenerImpl taskStackListener, - UiEventLogger uiEventLogger, - ShellTaskOrganizer organizer, - DisplayController displayController, - Optional<OneHandedController> oneHandedOptional, - DragAndDropController dragAndDropController, - @ShellMainThread ShellExecutor mainExecutor, - @ShellMainThread Handler mainHandler, - @ShellBackgroundThread ShellExecutor bgExecutor, - TaskViewTransitions taskViewTransitions, - SyncTransactionQueue syncQueue) { - BubbleLogger logger = new BubbleLogger(uiEventLogger); - BubblePositioner positioner = new BubblePositioner(context, windowManager); - BubbleData data = new BubbleData(context, logger, positioner, mainExecutor); - return new BubbleController(context, data, synchronizer, floatingContentCoordinator, - new BubbleDataRepository(context, launcherApps, mainExecutor), - statusBarService, windowManager, windowManagerShellWrapper, userManager, - launcherApps, logger, taskStackListener, organizer, positioner, displayController, - oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor, - taskViewTransitions, syncQueue); - } - - /** - * Testing constructor. - */ - @VisibleForTesting - protected BubbleController(Context context, + + public BubbleController(Context context, + ShellController shellController, BubbleData data, @Nullable BubbleStackView.SurfaceSynchronizer synchronizer, FloatingContentCoordinator floatingContentCoordinator, @@ -283,6 +250,7 @@ public class BubbleController { TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue) { mContext = context; + mShellController = shellController; mLauncherApps = launcherApps; mBarService = statusBarService == null ? IStatusBarService.Stub.asInterface( @@ -451,6 +419,8 @@ public class BubbleController { // Clear out any persisted bubbles on disk that no longer have a valid user. List<UserInfo> users = mUserManager.getAliveUsers(); mDataRepository.sanitizeBubbles(users); + + mShellController.addConfigurationChangeListener(this); } @VisibleForTesting @@ -835,7 +805,8 @@ public class BubbleController { mSavedBubbleKeysPerUser.remove(userId); } - private void updateForThemeChanges() { + @Override + public void onThemeChanged() { if (mStackView != null) { mStackView.onThemeChanged(); } @@ -855,7 +826,8 @@ public class BubbleController { } } - private void onConfigChanged(Configuration newConfig) { + @Override + public void onConfigurationChanged(Configuration newConfig) { if (mBubblePositioner != null) { mBubblePositioner.update(); } @@ -1715,13 +1687,6 @@ public class BubbleController { } @Override - public void updateForThemeChanges() { - mMainExecutor.execute(() -> { - BubbleController.this.updateForThemeChanges(); - }); - } - - @Override public void expandStackAndSelectBubble(BubbleEntry entry) { mMainExecutor.execute(() -> { BubbleController.this.expandStackAndSelectBubble(entry); @@ -1860,13 +1825,6 @@ public class BubbleController { } @Override - public void onConfigChanged(Configuration newConfig) { - mMainExecutor.execute(() -> { - BubbleController.this.onConfigChanged(newConfig); - }); - } - - @Override public void onNotificationPanelExpandedChanged(boolean expanded) { mMainExecutor.execute( () -> BubbleController.this.onNotificationPanelExpandedChanged(expanded)); 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 0072da19b9ef..f8ccf2364b4c 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 @@ -107,9 +107,6 @@ public interface Bubbles { /** Tell the stack of bubbles to collapse. */ void collapseStack(); - /** Tell the controller need update its UI to fit theme. */ - void updateForThemeChanges(); - /** * Request the stack expand if needed, then select the specified Bubble as current. * If no bubble exists for this entry, one is created. @@ -255,13 +252,6 @@ public interface Bubbles { */ void onUserRemoved(int removedUserId); - /** - * Called when config changed. - * - * @param newConfig the new config. - */ - void onConfigChanged(Configuration newConfig); - /** Description of current bubble state. */ void dump(PrintWriter pw, String[] args); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index 6a2acf438302..b3f62477077c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.IntDef; +import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.graphics.Point; @@ -324,7 +325,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged } @Override - public void topFocusedWindowChanged(String packageName, + public void topFocusedWindowChanged(ComponentName component, InsetsVisibilities requestedVisibilities) { // Do nothing } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java index b6705446674a..f546f110e87c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayInsetsController.java @@ -16,6 +16,7 @@ package com.android.wm.shell.common; +import android.content.ComponentName; import android.os.RemoteException; import android.util.Slog; import android.util.SparseArray; @@ -171,14 +172,14 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan } } - private void topFocusedWindowChanged(String packageName, + private void topFocusedWindowChanged(ComponentName component, InsetsVisibilities requestedVisibilities) { CopyOnWriteArrayList<OnInsetsChangedListener> listeners = mListeners.get(mDisplayId); if (listeners == null) { return; } for (OnInsetsChangedListener listener : listeners) { - listener.topFocusedWindowChanged(packageName, requestedVisibilities); + listener.topFocusedWindowChanged(component, requestedVisibilities); } } @@ -186,10 +187,10 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan private class DisplayWindowInsetsControllerImpl extends IDisplayWindowInsetsController.Stub { @Override - public void topFocusedWindowChanged(String packageName, + public void topFocusedWindowChanged(ComponentName component, InsetsVisibilities requestedVisibilities) throws RemoteException { mMainExecutor.execute(() -> { - PerDisplay.this.topFocusedWindowChanged(packageName, requestedVisibilities); + PerDisplay.this.topFocusedWindowChanged(component, requestedVisibilities); }); } @@ -234,10 +235,10 @@ public class DisplayInsetsController implements DisplayController.OnDisplaysChan /** * Called when top focused window changes to determine whether or not to take over insets * control. Won't be called if config_remoteInsetsControllerControlsSystemBars is false. - * @param packageName The name of the package that is open in the top focussed window. + * @param component The application component that is open in the top focussed window. * @param requestedVisibilities The insets visibilities requested by the focussed window. */ - default void topFocusedWindowChanged(String packageName, + default void topFocusedWindowChanged(ComponentName component, InsetsVisibilities requestedVisibilities) {} /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 5ebe384d84b7..8bc16bcc9d9d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -206,12 +206,12 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mSplitLayout = layout; mSplitWindowManager = splitWindowManager; mViewHost = viewHost; - mDividerBounds.set(layout.getDividerBounds()); + layout.getDividerBounds(mDividerBounds); onInsetsChanged(insetsState, false /* animate */); } void onInsetsChanged(InsetsState insetsState, boolean animate) { - mTempRect.set(mSplitLayout.getDividerBounds()); + mSplitLayout.getDividerBounds(mTempRect); 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 diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java index 484294ab295b..74f8bf9ac863 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitDecorManager.java @@ -50,12 +50,15 @@ import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; import com.android.wm.shell.common.SurfaceUtils; +import java.util.function.Consumer; + /** * Handles split decor like showing resizing hint for a specific split. */ public class SplitDecorManager extends WindowlessWindowManager { private static final String TAG = SplitDecorManager.class.getSimpleName(); private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground"; + private static final String GAP_BACKGROUND_SURFACE_NAME = "GapBackground"; private static final long FADE_DURATION = 133; private final IconProvider mIconProvider; @@ -67,6 +70,7 @@ public class SplitDecorManager extends WindowlessWindowManager { private SurfaceControl mHostLeash; private SurfaceControl mIconLeash; private SurfaceControl mBackgroundLeash; + private SurfaceControl mGapBackgroundLeash; private boolean mShown; private boolean mIsResizing; @@ -141,6 +145,10 @@ public class SplitDecorManager extends WindowlessWindowManager { t.remove(mBackgroundLeash); mBackgroundLeash = null; } + if (mGapBackgroundLeash != null) { + t.remove(mGapBackgroundLeash); + mGapBackgroundLeash = null; + } mHostLeash = null; mIcon = null; mResizingIconView = null; @@ -150,7 +158,7 @@ public class SplitDecorManager extends WindowlessWindowManager { /** Showing resizing hint. */ public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds, - SurfaceControl.Transaction t) { + Rect sideBounds, SurfaceControl.Transaction t) { if (mResizingIconView == null) { return; } @@ -176,6 +184,19 @@ public class SplitDecorManager extends WindowlessWindowManager { .setLayer(mBackgroundLeash, Integer.MAX_VALUE - 1); } + if (mGapBackgroundLeash == null) { + final boolean isLandscape = newBounds.height() == sideBounds.height(); + final int left = isLandscape ? mBounds.width() : 0; + final int top = isLandscape ? 0 : mBounds.height(); + mGapBackgroundLeash = SurfaceUtils.makeColorLayer(mHostLeash, + GAP_BACKGROUND_SURFACE_NAME, mSurfaceSession); + // Fill up another side bounds area. + t.setColor(mGapBackgroundLeash, getResizingBackgroundColor(resizingTask)) + .setLayer(mGapBackgroundLeash, Integer.MAX_VALUE - 2) + .setPosition(mGapBackgroundLeash, left, top) + .setWindowCrop(mGapBackgroundLeash, sideBounds.width(), sideBounds.height()); + } + if (mIcon == null && resizingTask.topActivityInfo != null) { mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo); mResizingIconView.setImageDrawable(mIcon); @@ -193,7 +214,7 @@ public class SplitDecorManager extends WindowlessWindowManager { newBounds.height() / 2 - mIconSize / 2); if (animate) { - startFadeAnimation(show, false /* isResized */); + startFadeAnimation(show, null /* finishedConsumer */); mShown = show; } } @@ -224,14 +245,29 @@ public class SplitDecorManager extends WindowlessWindowManager { mFadeAnimator.cancel(); } if (mShown) { - startFadeAnimation(false /* show */, true /* isResized */); + fadeOutDecor(null /* finishedCallback */); } else { // Decor surface is hidden so release it directly. releaseDecor(t); } } - private void startFadeAnimation(boolean show, boolean isResized) { + /** Fade-out decor surface with animation end callback, if decor is hidden, run the callback + * directly. */ + public void fadeOutDecor(Runnable finishedCallback) { + if (mShown) { + startFadeAnimation(false /* show */, transaction -> { + releaseDecor(transaction); + if (finishedCallback != null) finishedCallback.run(); + }); + mShown = false; + } else { + if (finishedCallback != null) finishedCallback.run(); + } + } + + private void startFadeAnimation(boolean show, + Consumer<SurfaceControl.Transaction> finishedConsumer) { final SurfaceControl.Transaction animT = new SurfaceControl.Transaction(); mFadeAnimator = ValueAnimator.ofFloat(0f, 1f); mFadeAnimator.setDuration(FADE_DURATION); @@ -249,7 +285,9 @@ public class SplitDecorManager extends WindowlessWindowManager { @Override public void onAnimationStart(@NonNull Animator animation) { if (show) { - animT.show(mBackgroundLeash).show(mIconLeash).apply(); + animT.show(mBackgroundLeash).show(mIconLeash).show(mGapBackgroundLeash).apply(); + } else { + animT.hide(mGapBackgroundLeash).apply(); } } @@ -263,8 +301,8 @@ public class SplitDecorManager extends WindowlessWindowManager { animT.hide(mIconLeash); } } - if (isResized) { - releaseDecor(animT); + if (finishedConsumer != null) { + finishedConsumer.accept(animT); } animT.apply(); animT.close(); @@ -280,6 +318,11 @@ public class SplitDecorManager extends WindowlessWindowManager { mBackgroundLeash = null; } + if (mGapBackgroundLeash != null) { + t.remove(mGapBackgroundLeash); + mGapBackgroundLeash = null; + } + if (mIcon != null) { mResizingIconView.setVisibility(View.GONE); mResizingIconView.setImageDrawable(null); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index c962de67a93b..40cf9a32d7a5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -32,6 +32,7 @@ import static com.android.wm.shell.animation.Interpolators.SLOWDOWN_INTERPOLATOR 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.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -200,6 +201,44 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange return outBounds; } + /** Gets bounds of the primary split with screen based coordinate on the param Rect. */ + public void getBounds1(Rect rect) { + rect.set(mBounds1); + } + + /** Gets bounds of the primary split with parent based coordinate on the param Rect. */ + public void getRefBounds1(Rect rect) { + getBounds1(rect); + rect.offset(-mRootBounds.left, -mRootBounds.top); + } + + /** Gets bounds of the secondary split with screen based coordinate on the param Rect. */ + public void getBounds2(Rect rect) { + rect.set(mBounds2); + } + + /** Gets bounds of the secondary split with parent based coordinate on the param Rect. */ + public void getRefBounds2(Rect rect) { + getBounds2(rect); + rect.offset(-mRootBounds.left, -mRootBounds.top); + } + + /** Gets root bounds of the whole split layout on the param Rect. */ + public void getRootBounds(Rect rect) { + rect.set(mRootBounds); + } + + /** Gets bounds of divider window with screen based coordinate on the param Rect. */ + public void getDividerBounds(Rect rect) { + rect.set(mDividerBounds); + } + + /** Gets bounds of divider window with parent based coordinate on the param Rect. */ + public void getRefDividerBounds(Rect rect) { + getDividerBounds(rect); + rect.offset(-mRootBounds.left, -mRootBounds.top); + } + /** Returns leash of the current divider bar. */ @Nullable public SurfaceControl getDividerLeash() { @@ -411,11 +450,13 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange switch (snapTarget.flag) { case FLAG_DISMISS_START: flingDividePosition(currentPosition, snapTarget.position, - () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */)); + () -> mSplitLayoutHandler.onSnappedToDismiss(false /* bottomOrRight */, + EXIT_REASON_DRAG_DIVIDER)); break; case FLAG_DISMISS_END: flingDividePosition(currentPosition, snapTarget.position, - () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */)); + () -> mSplitLayoutHandler.onSnappedToDismiss(true /* bottomOrRight */, + EXIT_REASON_DRAG_DIVIDER)); break; default: flingDividePosition(currentPosition, snapTarget.position, @@ -449,16 +490,36 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange private DividerSnapAlgorithm getSnapAlgorithm(Context context, Rect rootBounds, @Nullable Rect stableInsets) { final boolean isLandscape = isLandscape(rootBounds); + final Rect insets = stableInsets != null ? stableInsets : getDisplayInsets(context); + + // Make split axis insets value same as the larger one to avoid bounds1 and bounds2 + // have difference after split switching for solving issues on non-resizable app case. + if (isLandscape) { + final int largerInsets = Math.max(insets.left, insets.right); + insets.set(largerInsets, insets.top, largerInsets, insets.bottom); + } else { + final int largerInsets = Math.max(insets.top, insets.bottom); + insets.set(insets.left, largerInsets, insets.right, largerInsets); + } + return new DividerSnapAlgorithm( context.getResources(), rootBounds.width(), rootBounds.height(), mDividerSize, !isLandscape, - stableInsets != null ? stableInsets : getDisplayInsets(context), + insets, isLandscape ? DOCKED_LEFT : DOCKED_TOP /* dockSide */); } + /** Fling divider from current position to end or start position then exit */ + public void flingDividerToDismiss(boolean toEnd, int reason) { + final int target = toEnd ? mDividerSnapAlgorithm.getDismissEndTarget().position + : mDividerSnapAlgorithm.getDismissStartTarget().position; + flingDividePosition(getDividePosition(), target, + () -> mSplitLayoutHandler.onSnappedToDismiss(toEnd, reason)); + } + @VisibleForTesting void flingDividePosition(int from, int to, @Nullable Runnable flingFinishedCallback) { if (from == to) { @@ -560,8 +621,8 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange t.setPosition(leash, distX, distY); t.setWindowCrop(leash, width, height); } else { - final int offsetX = width - start.width(); - final int offsetY = height - start.height(); + final int offsetX = width - tempStart.width(); + final int offsetY = height - tempStart.height(); t.setPosition(leash, distX + offsetX, distY + offsetY); mTempRect.set(0, 0, width, height); mTempRect.offsetTo(-offsetX, -offsetY); @@ -610,15 +671,15 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange boolean applyResizingOffset) { final SurfaceControl dividerLeash = getDividerLeash(); if (dividerLeash != null) { - mTempRect.set(getRefDividerBounds()); + getRefDividerBounds(mTempRect); t.setPosition(dividerLeash, mTempRect.left, mTempRect.top); // Resets layer of divider bar to make sure it is always on top. t.setLayer(dividerLeash, Integer.MAX_VALUE); } - mTempRect.set(getRefBounds1()); + getRefBounds1(mTempRect); t.setPosition(leash1, mTempRect.left, mTempRect.top) .setWindowCrop(leash1, mTempRect.width(), mTempRect.height()); - mTempRect.set(getRefBounds2()); + getRefBounds2(mTempRect); t.setPosition(leash2, mTempRect.left, mTempRect.top) .setWindowCrop(leash2, mTempRect.width(), mTempRect.height()); @@ -666,31 +727,23 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange ActivityManager.RunningTaskInfo taskInfo1, ActivityManager.RunningTaskInfo taskInfo2) { if (offsetX == 0 && offsetY == 0) { wct.setBounds(taskInfo1.token, mBounds1); - wct.setAppBounds(taskInfo1.token, null); wct.setScreenSizeDp(taskInfo1.token, SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); wct.setBounds(taskInfo2.token, mBounds2); - wct.setAppBounds(taskInfo2.token, null); wct.setScreenSizeDp(taskInfo2.token, SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED); } else { - mTempRect.set(taskInfo1.configuration.windowConfiguration.getBounds()); + getBounds1(mTempRect); mTempRect.offset(offsetX, offsetY); wct.setBounds(taskInfo1.token, mTempRect); - mTempRect.set(taskInfo1.configuration.windowConfiguration.getAppBounds()); - mTempRect.offset(offsetX, offsetY); - wct.setAppBounds(taskInfo1.token, mTempRect); wct.setScreenSizeDp(taskInfo1.token, taskInfo1.configuration.screenWidthDp, taskInfo1.configuration.screenHeightDp); - mTempRect.set(taskInfo2.configuration.windowConfiguration.getBounds()); + getBounds2(mTempRect); mTempRect.offset(offsetX, offsetY); wct.setBounds(taskInfo2.token, mTempRect); - mTempRect.set(taskInfo2.configuration.windowConfiguration.getAppBounds()); - mTempRect.offset(offsetX, offsetY); - wct.setAppBounds(taskInfo2.token, mTempRect); wct.setScreenSizeDp(taskInfo2.token, taskInfo2.configuration.screenWidthDp, taskInfo2.configuration.screenHeightDp); @@ -708,7 +761,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange public interface SplitLayoutHandler { /** Calls when dismissing split. */ - void onSnappedToDismiss(boolean snappedToEnd); + void onSnappedToDismiss(boolean snappedToEnd, int reason); /** * Calls when resizing the split bounds. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java index 9b614875119b..afc706ee9c8e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenConstants.java @@ -15,6 +15,11 @@ */ package com.android.wm.shell.common.split; +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 android.annotation.IntDef; /** Helper utility class of methods and constants that are available to be imported in Launcher. */ @@ -44,4 +49,10 @@ public class SplitScreenConstants { }) public @interface SplitPosition { } + + public static final int[] CONTROLLED_ACTIVITY_TYPES = {ACTIVITY_TYPE_STANDARD}; + public static final int[] CONTROLLED_WINDOWING_MODES = + {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED}; + public static final int[] CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE = + {WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED, WINDOWING_MODE_MULTI_WINDOW}; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 99b32a677abe..db8d9d423aca 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -39,9 +39,10 @@ import com.android.wm.shell.common.DisplayInsetsController.OnInsetsChangedListen 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.annotations.ExternalThread; import com.android.wm.shell.compatui.CompatUIWindowManager.CompatUIHintsState; import com.android.wm.shell.compatui.letterboxedu.LetterboxEduWindowManager; +import com.android.wm.shell.sysui.KeyguardChangeListener; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.transition.Transitions; import java.lang.ref.WeakReference; @@ -58,7 +59,7 @@ import dagger.Lazy; * activities are in compatibility mode. */ public class CompatUIController implements OnDisplaysChangedListener, - DisplayImeController.ImePositionProcessor { + DisplayImeController.ImePositionProcessor, KeyguardChangeListener { /** Callback for compat UI interaction. */ public interface CompatUICallback { @@ -100,13 +101,13 @@ public class CompatUIController implements OnDisplaysChangedListener, private final SparseArray<WeakReference<Context>> mDisplayContextCache = new SparseArray<>(0); private final Context mContext; + private final ShellController mShellController; private final DisplayController mDisplayController; private final DisplayInsetsController mDisplayInsetsController; private final DisplayImeController mImeController; private final SyncTransactionQueue mSyncQueue; private final ShellExecutor mMainExecutor; private final Lazy<Transitions> mTransitionsLazy; - private final CompatUIImpl mImpl = new CompatUIImpl(); private CompatUICallback mCallback; @@ -118,6 +119,7 @@ public class CompatUIController implements OnDisplaysChangedListener, private boolean mKeyguardShowing; public CompatUIController(Context context, + ShellController shellController, DisplayController displayController, DisplayInsetsController displayInsetsController, DisplayImeController imeController, @@ -125,6 +127,7 @@ public class CompatUIController implements OnDisplaysChangedListener, ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy) { mContext = context; + mShellController = shellController; mDisplayController = displayController; mDisplayInsetsController = displayInsetsController; mImeController = imeController; @@ -134,11 +137,7 @@ public class CompatUIController implements OnDisplaysChangedListener, mDisplayController.addDisplayWindowListener(this); mImeController.addPositionProcessor(this); mCompatUIHintsState = new CompatUIHintsState(); - } - - /** Returns implementation of {@link CompatUI}. */ - public CompatUI asCompatUI() { - return mImpl; + shellController.addKeyguardChangeListener(this); } /** Sets the callback for UI interactions. */ @@ -223,9 +222,10 @@ public class CompatUIController implements OnDisplaysChangedListener, layout -> layout.updateVisibility(showOnDisplay(displayId))); } - @VisibleForTesting - void onKeyguardShowingChanged(boolean showing) { - mKeyguardShowing = showing; + @Override + public void onKeyguardVisibilityChanged(boolean visible, boolean occluded, + boolean animatingDismiss) { + mKeyguardShowing = visible; // Hide the compat UIs when keyguard is showing. forAllLayouts(layout -> layout.updateVisibility(showOnDisplay(layout.getDisplayId()))); } @@ -373,19 +373,6 @@ public class CompatUIController implements OnDisplaysChangedListener, } } - /** - * The interface for calls from outside the Shell, within the host process. - */ - @ExternalThread - private class CompatUIImpl implements CompatUI { - @Override - public void onKeyguardShowingChanged(boolean showing) { - mMainExecutor.execute(() -> { - CompatUIController.this.onKeyguardShowingChanged(showing); - }); - } - } - /** An implementation of {@link OnInsetsChangedListener} for a given display id. */ private class PerDisplayOnInsetsChangedListener implements OnInsetsChangedListener { final int mDisplayId; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt deleted file mode 100644 index 1cd69edf7cd2..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/README.txt +++ /dev/null @@ -1,13 +0,0 @@ -The dagger modules in this directory can be included by the host SysUI using the Shell library for -explicity injection of Shell components. Apps using this library are not required to use these -dagger modules for setup, but it is recommended for them to include them as needed. - -The modules are currently inherited as such: - -+- WMShellBaseModule (common shell features across SysUI) - | - +- WMShellModule (handheld) - | - +- TvPipModule (tv pip) - | - +- TvWMShellModule (tv)
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java index 1ea5e21a2c1e..81904e291ad1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java @@ -48,6 +48,7 @@ import com.android.wm.shell.pip.tv.TvPipNotificationController; import com.android.wm.shell.pip.tv.TvPipTaskOrganizer; import com.android.wm.shell.pip.tv.TvPipTransition; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.transition.Transitions; import java.util.Optional; @@ -64,6 +65,7 @@ public abstract class TvPipModule { @Provides static Optional<Pip> providePip( Context context, + ShellController shellController, TvPipBoundsState tvPipBoundsState, TvPipBoundsAlgorithm tvPipBoundsAlgorithm, TvPipBoundsController tvPipBoundsController, @@ -81,6 +83,7 @@ public abstract class TvPipModule { return Optional.of( TvPipController.create( context, + shellController, tvPipBoundsState, tvPipBoundsAlgorithm, tvPipBoundsController, 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 2ea111b113d8..586eab07649f 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,6 +38,7 @@ 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.activityembedding.ActivityEmbeddingController; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.back.BackAnimationController; import com.android.wm.shell.bubbles.BubbleController; @@ -56,15 +57,12 @@ import com.android.wm.shell.common.annotations.ShellAnimationThread; import com.android.wm.shell.common.annotations.ShellBackgroundThread; import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; -import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.compatui.CompatUIController; import com.android.wm.shell.displayareahelper.DisplayAreaHelper; import com.android.wm.shell.displayareahelper.DisplayAreaHelperController; -import com.android.wm.shell.draganddrop.DragAndDrop; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.fullscreen.FullscreenTaskListener; -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.onehanded.OneHanded; @@ -82,8 +80,8 @@ import com.android.wm.shell.startingsurface.StartingSurface; import com.android.wm.shell.startingsurface.StartingWindowController; import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm; import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm; -import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper; -import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelperController; +import com.android.wm.shell.sysui.ShellController; +import com.android.wm.shell.sysui.ShellInterface; import com.android.wm.shell.transition.ShellTransitions; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; @@ -159,16 +157,13 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static DragAndDropController provideDragAndDropController(Context context, - DisplayController displayController, UiEventLogger uiEventLogger, - IconProvider iconProvider, @ShellMainThread ShellExecutor mainExecutor) { - return new DragAndDropController(context, displayController, uiEventLogger, iconProvider, - mainExecutor); - } - - @WMSingleton - @Provides - static Optional<DragAndDrop> provideDragAndDrop(DragAndDropController dragAndDropController) { - return Optional.of(dragAndDropController.asDragAndDrop()); + ShellController shellController, + DisplayController displayController, + UiEventLogger uiEventLogger, + IconProvider iconProvider, + @ShellMainThread ShellExecutor mainExecutor) { + return new DragAndDropController(context, shellController, displayController, uiEventLogger, + iconProvider, mainExecutor); } @WMSingleton @@ -201,18 +196,14 @@ public abstract class WMShellBaseModule { } @WMSingleton - @Provides static Optional<CompatUI> provideCompatUI(CompatUIController compatUIController) { - return Optional.of(compatUIController.asCompatUI()); - } - - @WMSingleton @Provides static CompatUIController provideCompatUIController(Context context, + ShellController shellController, DisplayController displayController, DisplayInsetsController displayInsetsController, DisplayImeController imeController, SyncTransactionQueue syncQueue, @ShellMainThread ShellExecutor mainExecutor, Lazy<Transitions> transitionsLazy) { - return new CompatUIController(context, displayController, displayInsetsController, - imeController, syncQueue, mainExecutor, transitionsLazy); + return new CompatUIController(context, shellController, displayController, + displayInsetsController, imeController, syncQueue, mainExecutor, transitionsLazy); } @WMSingleton @@ -369,17 +360,12 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides - static Optional<HideDisplayCutout> provideHideDisplayCutout( - Optional<HideDisplayCutoutController> hideDisplayCutoutController) { - return hideDisplayCutoutController.map((controller) -> controller.asHideDisplayCutout()); - } - - @WMSingleton - @Provides static Optional<HideDisplayCutoutController> provideHideDisplayCutoutController(Context context, - DisplayController displayController, @ShellMainThread ShellExecutor mainExecutor) { + ShellController shellController, DisplayController displayController, + @ShellMainThread ShellExecutor mainExecutor) { return Optional.ofNullable( - HideDisplayCutoutController.create(context, displayController, mainExecutor)); + HideDisplayCutoutController.create(context, shellController, displayController, + mainExecutor)); } // @@ -408,23 +394,6 @@ public abstract class WMShellBaseModule { } // - // Task to Surface communication - // - - @WMSingleton - @Provides - static Optional<TaskSurfaceHelper> provideTaskSurfaceHelper( - Optional<TaskSurfaceHelperController> taskSurfaceController) { - return taskSurfaceController.map((controller) -> controller.asTaskSurfaceHelper()); - } - - @Provides - static Optional<TaskSurfaceHelperController> provideTaskSurfaceHelperController( - ShellTaskOrganizer taskOrganizer, @ShellMainThread ShellExecutor mainExecutor) { - return Optional.ofNullable(new TaskSurfaceHelperController(taskOrganizer, mainExecutor)); - } - - // // Pip (optional feature) // @@ -621,6 +590,34 @@ public abstract class WMShellBaseModule { taskViewTransitions); } + + // + // ActivityEmbedding + // + + @WMSingleton + @Provides + static Optional<ActivityEmbeddingController> provideActivityEmbeddingController( + Context context, Transitions transitions) { + return Optional.of(new ActivityEmbeddingController(context, transitions)); + } + + // + // SysUI -> Shell interface + // + + @WMSingleton + @Provides + static ShellInterface provideShellSysuiCallbacks(ShellController shellController) { + return shellController.asShell(); + } + + @WMSingleton + @Provides + static ShellController provideShellController(@ShellMainThread ShellExecutor mainExecutor) { + return new ShellController(mainExecutor); + } + // // Misc // @@ -647,6 +644,7 @@ public abstract class WMShellBaseModule { Optional<UnfoldTransitionHandler> unfoldTransitionHandler, Optional<FreeformTaskListener<?>> freeformTaskListener, Optional<RecentTasksController> recentTasksOptional, + Optional<ActivityEmbeddingController> activityEmbeddingOptional, Transitions transitions, StartingWindowController startingWindow, @ShellMainThread ShellExecutor mainExecutor) { @@ -664,6 +662,7 @@ public abstract class WMShellBaseModule { unfoldTransitionHandler, freeformTaskListener, recentTasksOptional, + activityEmbeddingOptional, transitions, startingWindow, mainExecutor); 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 a8c1071eb69e..0f33f4c08e9c 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 @@ -31,6 +31,10 @@ import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.TaskViewTransitions; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.bubbles.BubbleController; +import com.android.wm.shell.bubbles.BubbleData; +import com.android.wm.shell.bubbles.BubbleDataRepository; +import com.android.wm.shell.bubbles.BubbleLogger; +import com.android.wm.shell.bubbles.BubblePositioner; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.DisplayInsetsController; @@ -67,6 +71,7 @@ import com.android.wm.shell.pip.phone.PipMotionHelper; 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.sysui.ShellController; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.unfold.ShellUnfoldProgressProvider; import com.android.wm.shell.unfold.UnfoldAnimationController; @@ -104,10 +109,34 @@ public abstract class WMShellModule { // Bubbles // + @WMSingleton + @Provides + static BubbleLogger provideBubbleLogger(UiEventLogger uiEventLogger) { + return new BubbleLogger(uiEventLogger); + } + + @WMSingleton + @Provides + static BubblePositioner provideBubblePositioner(Context context, + WindowManager windowManager) { + return new BubblePositioner(context, windowManager); + } + + @WMSingleton + @Provides + static BubbleData provideBubbleData(Context context, + BubbleLogger logger, + BubblePositioner positioner, + @ShellMainThread ShellExecutor mainExecutor) { + return new BubbleData(context, logger, positioner, mainExecutor); + } + // Note: Handler needed for LauncherApps.register @WMSingleton @Provides static BubbleController provideBubbleController(Context context, + ShellController shellController, + BubbleData data, FloatingContentCoordinator floatingContentCoordinator, IStatusBarService statusBarService, WindowManager windowManager, @@ -115,8 +144,9 @@ public abstract class WMShellModule { UserManager userManager, LauncherApps launcherApps, TaskStackListenerImpl taskStackListener, - UiEventLogger uiEventLogger, + BubbleLogger logger, ShellTaskOrganizer organizer, + BubblePositioner positioner, DisplayController displayController, @DynamicOverride Optional<OneHandedController> oneHandedOptional, DragAndDropController dragAndDropController, @@ -125,11 +155,12 @@ public abstract class WMShellModule { @ShellBackgroundThread ShellExecutor bgExecutor, TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue) { - return BubbleController.create(context, null /* synchronizer */, - floatingContentCoordinator, statusBarService, windowManager, - windowManagerShellWrapper, userManager, launcherApps, taskStackListener, - uiEventLogger, organizer, displayController, oneHandedOptional, - dragAndDropController, mainExecutor, mainHandler, bgExecutor, + return new BubbleController(context, shellController, data, null /* synchronizer */, + floatingContentCoordinator, + new BubbleDataRepository(context, launcherApps, mainExecutor), + statusBarService, windowManager, windowManagerShellWrapper, userManager, + launcherApps, logger, taskStackListener, organizer, positioner, displayController, + oneHandedOptional, dragAndDropController, mainExecutor, mainHandler, bgExecutor, taskViewTransitions, syncQueue); } @@ -176,12 +207,14 @@ public abstract class WMShellModule { @Provides @DynamicOverride static OneHandedController provideOneHandedController(Context context, + ShellController shellController, WindowManager windowManager, DisplayController displayController, DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener, UiEventLogger uiEventLogger, InteractionJankMonitor jankMonitor, @ShellMainThread ShellExecutor mainExecutor, @ShellMainThread Handler mainHandler) { - return OneHandedController.create(context, windowManager, displayController, displayLayout, - taskStackListener, jankMonitor, uiEventLogger, mainExecutor, mainHandler); + return OneHandedController.create(context, shellController, windowManager, + displayController, displayLayout, taskStackListener, jankMonitor, uiEventLogger, + mainExecutor, mainHandler); } // @@ -192,6 +225,7 @@ public abstract class WMShellModule { @Provides @DynamicOverride static SplitScreenController provideSplitScreenController( + ShellController shellController, ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Context context, RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, @@ -201,7 +235,7 @@ public abstract class WMShellModule { DisplayInsetsController displayInsetsController, Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider, Optional<RecentTasksController> recentTasks) { - return new SplitScreenController(shellTaskOrganizer, syncQueue, context, + return new SplitScreenController(shellController, shellTaskOrganizer, syncQueue, context, rootTaskDisplayAreaOrganizer, mainExecutor, displayController, displayImeController, displayInsetsController, transitions, transactionPool, iconProvider, recentTasks); @@ -213,7 +247,8 @@ public abstract class WMShellModule { @WMSingleton @Provides - static Optional<Pip> providePip(Context context, DisplayController displayController, + static Optional<Pip> providePip(Context context, + ShellController shellController, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState, PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, @@ -225,7 +260,7 @@ public abstract class WMShellModule { PipParamsChangedForwarder pipParamsChangedForwarder, Optional<OneHandedController> oneHandedController, @ShellMainThread ShellExecutor mainExecutor) { - return Optional.ofNullable(PipController.create(context, displayController, + return Optional.ofNullable(PipController.create(context, shellController, displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md new file mode 100644 index 000000000000..73a7348d5aca --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/README.md @@ -0,0 +1,18 @@ +# Window Manager Shell Readme + +The following docs present more detail about the implementation of the WMShell library (in no +particular order): + +1) [What is the Shell](overview.md) +2) [Integration with SystemUI & Launcher](sysui.md) +3) [Usage of Dagger](dagger.md) +4) [Threading model in the Shell](threading.md) +5) [Making changes in the Shell](changes.md) +6) [Extending the Shell for Products/OEMs](extending.md) +7) [Debugging in the Shell](debugging.md) +8) [Testing in the Shell](testing.md) + +Todo +- Per-feature docs +- Feature flagging +- Best practices
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md new file mode 100644 index 000000000000..f4e2f20f4637 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/changes.md @@ -0,0 +1,73 @@ +# Making changes in the Shell + +--- + +## Code reviews + +In addition to the individual reviewers who are most familiar with the changes you are making, +please also add [wm-code-reviewers@google.com](http://g/wm-code-reviewers) to keep other WM folks +in the loop. + +## Adding new code + +### Internal Shell utility classes +If the new component is used only within the WMShell library, then there are no special +considerations, go ahead and add it (in the `com.android.wm.shell.common` package for example) +and make sure the appropriate [unit tests](testing.md) are added. + +### Internal Shell components +If the new component is to be used by other components/features within the Shell library, then +you can create an appropriate package for this component to add your new code. The current +pattern is to have a single `<Component name>Controller` that handles the initialization of the +component. + +As mentioned in the [Dagger usage](dagger.md) docs, you need to determine whether it should go into: +- `WMShellBaseModule` for components that other base & product components will depend on +- or `WMShellModule`, `TvWmShellModule`, etc. for product specific components that no base + components depend on + +### SysUI accessible components +In addition to doing the above, you will also need to provide an interface for calling to SysUI +from the Shell and vice versa. The current pattern is to have a parallel `Optional<Component name>` +interface that the `<Component name>Controller` implements and handles on the main Shell thread. + +In addition, because components accessible to SysUI injection are explicitly listed, you'll have to +add an appropriate method in `WMComponent` to get the interface and update the `Builder` in +`SysUIComponent` to take the interface so it can be injected in SysUI code. The binding between +the two is done in `SystemUIFactory#init()` which will need to be updated as well. + +### Launcher accessible components +Because Launcher is not a part of SystemUI and is a separate process, exposing controllers to +Launcher requires a new AIDL interface to be created and implemented by the controller. The +implementation of the stub interface in the controller otherwise behaves similar to the interface +to SysUI where it posts the work to the main Shell thread. + +### Component initialization +To initialize the component: +- On the Shell side, update `ShellInitImpl` to get a signal to initialize when the SysUI is started +- On the SysUI side, update `WMShell` to setup any bindings for the component that depend on + SysUI code + +### General Do's & Dont's +Do: +- Do add unit tests for all new components +- Do keep controllers simple and break them down as needed + +Don't: +- **Don't** do initialization in the constructor, only do initialization in the init callbacks. + Otherwise it complicates the building of the dependency graph. +- **Don't** create dependencies from base-module components on specific features (the base module + is intended for use with all products) + - Try adding a mechanism to register and listen for changes from the base module component instead +- **Don't** add blocking synchronous calls in the SysUI interface between Shell & SysUI + - Try adding a push-mechanism to share data, or an async callback to request data + +### Exposing shared code for use in Launcher +Launcher doesn't currently build against the Shell library, but needs to have access to some shared +AIDL interfaces and constants. Currently, all AIDL files, and classes under the +`com.android.wm.shell.util` package are automatically built into the `SystemUISharedLib` that +Launcher uses. + +If the new code doesn't fall into those categories, they can be added explicitly in the Shell's +[Android.bp](frameworks/base/libs/WindowManager/Shell/Android.bp) file under the +`wm_shell_util-sources` filegroup.
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/dagger.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/dagger.md new file mode 100644 index 000000000000..6c01d962adc9 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/dagger.md @@ -0,0 +1,50 @@ +# Usage of Dagger in the Shell library + +--- + +## Dependencies + +Dagger is not required to use the Shell library, but it has a lot of obvious benefits: + +- Not having to worry about how to instantiate all the dependencies of a class, especially as + dependencies evolve (ie. product controller depends on base controller) +- Can create boundaries within the same app to encourage better code modularity + +As such, the Shell also tries to provide some reasonable out-of-the-box modules for use with Dagger. + +## Modules + +All the Dagger related code in the Shell can be found in the `com.android.wm.shell.dagger` package, +this is intentional as it keeps the "magic" in a single location. The explicit nature of how +components in the shell are provided is as a result a bit more verbose, but it makes it easy for +developers to jump into a few select files and understand how different components are provided +(especially as products override components). + +The module dependency tree looks a bit like: +- [WMShellConcurrencyModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java) + (provides threading-related components) + - [WMShellBaseModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java) + (provides components that are likely common to all products, ie. DisplayController, + Transactions, etc.) + - [WMShellModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java) + (phone/tablet specific components only) + - [TvPipModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvPipModule.java) + (PIP specific components for TV) + - [TvWMShellModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java) + (TV specific components only) + - etc. + +Ideally features could be abstracted out into their own modules and included as needed by each +product. + +## Overriding base components + +In some rare cases, there are base components that can change behavior depending on which +product it runs on. If there are hooks that can be added to the component, that is the +preferable approach. + +The alternative is to use the [@DynamicOverride](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/DynamicOverride.java) +annotation to allow the product module to provide an implementation that the base module can +reference. This is most useful if the existence of the entire component is controlled by the +product and the override implementation is optional (there is a default implementation). More +details can be found in the class's javadoc.
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md new file mode 100644 index 000000000000..52f0c4222b64 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/debugging.md @@ -0,0 +1,69 @@ +# Debugging in the Shell + +--- + +## Logging & ProtoLogs + +The interactions in the Shell can be pretty complicated, so having good logging is crucial to +debugging problems that arise (especially in dogfood). The Shell uses the same efficient Protolog +mechanism as WM Core, which can be enabled at runtime on debug devices. + +**TLDR** Don’t use Logs or Slogs except for error cases, Protologs are much more flexible, +easy to add and easy to use + +### Adding a new ProtoLog +Update `ShellProtoLogGroup` to include a new log group (ie. NEW_FEATURE) for the content you want to +log. ProtoLog log calls mirror Log.v/d/e(), and take a format message and arguments: +```java +ProtoLog.v(NEW_FEATURE, "Test log w/ params: %d %s", 1, “a”) +``` +This code itself will not compile by itself, but the `protologtool` will preprocess the file when +building to check the log state (is enabled) before printing the print format style log. + +**Notes** +- ProtoLogs currently only work from soong builds (ie. via make/mp). We need to reimplement the + tool for use with SysUI-studio +- Non-text ProtoLogs are not currently supported with the Shell library (you can't view them with + traces in Winscope) + +### Enabling ProtoLog command line logging +Run these commands to enable protologs for both WM Core and WM Shell to print to logcat. +```shell +adb shell wm logging enable-text NEW_FEATURE +adb shell wm logging disable-text NEW_FEATURE +``` + +## Winscope Tracing + +The Winscope tool is extremely useful in determining what is happening on-screen in both +WindowManager and SurfaceFlinger. Follow [go/winscope](http://go/winscope-help) to learn how to +use the tool. + +In addition, there is limited preliminary support for Winscope tracing componetns in the Shell, +which involves adding trace fields to [wm_shell_trace.proto](frameworks/base/libs/WindowManager/Shell/proto/wm_shell_trace.proto) +file and ensure it is updated as a part of `WMShell#writeToProto`. + +Tracing can be started via the shell command (to be added to the Winscope tool as needed): +```shell +adb shell cmd statusbar tracing start +adb shell cmd statusbar tracing stop +``` + +## Dumps + +Because the Shell library is built as a part of SystemUI, dumping the state is currently done as a +part of dumping the SystemUI service. Dumping the Shell specific data can be done by specifying the +WMShell SysUI service: + +```shell +adb shell dumpsys activity service SystemUIService WMShell +``` + +If information should be added to the dump, make updates to: +- `WMShell` if you are dumping SysUI state +- `ShellCommandHandler` if you are dumping Shell state + +## Debugging in Android Studio + +If you are using the [go/sysui-studio](http://go/sysui-studio) project, then you can debug Shell +code directly from Android Studio like any other app. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/extending.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/extending.md new file mode 100644 index 000000000000..061ae00e2b25 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/extending.md @@ -0,0 +1,13 @@ +# Extending the Shell for Products/OEMs + +--- + +## General Do's & Dont's + +Do: +- + +Don't +- **Don't** override classes provided by WMShellBaseModule, it makes it difficult to make + simple changes to the Shell library base modules which are shared by all products + - If possible add mechanisms to modify the base class behavior
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/overview.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/overview.md new file mode 100644 index 000000000000..a88ef6aea2ec --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/overview.md @@ -0,0 +1,58 @@ +# What is the WindowManager Shell + +--- + +## Motivation + +The primary motivation for the WindowManager Shell (WMShell) library is to effectively scale +WindowManager by making it easy™ and safe to create windowing features to fit the needs of +various Android products and form factors. + +To achieve this, WindowManager separates the policy of managing windows (WMCore) from the +presentation of surfaces (WMShell) and provides a minimal interface boundary for the two to +communicate. + +## Who is using the library? + +Currently, the WMShell library is used to drive the windowing experience on handheld +(phones & tablets), TV, Auto, Arc++, and Wear to varying degrees. + +## Where does the code live + +The core WMShell library code is currently located in the [frameworks/base/libs/WindowManager/Shell](frameworks/base/libs/WindowManager/Shell) +directory and is included as a part dependency of the host SystemUI apk. + +## How do I build the Shell library + +The library can be built directly by running (using [go/makepush](http://go/makepush)): +```shell +mp :WindowManager-Shell +``` +But this is mainly useful for inspecting the contents of the library or verifying it builds. The +various targets can be found in the Shell library's [Android.bp](frameworks/base/libs/WindowManager/Shell/Android.bp) +file. + +Normally, you would build it as a part of the host SystemUI, for example via commandline: +```shell +# Phone SystemUI variant +mp sysuig +# Building Shell & SysUI changes along w/ framework changes +mp core services sysuig +``` + +Or preferably, if you are making WMShell/SysUI only changes (no other framework changes), then +building via [go/sysui-studio](http://go/sysui-studio) allows for very quick iteration (one click +build and push of SysUI in < 30s). + +If you are making framework changes and are using `aidegen` to set up your platform IDE, make sure +to include the appropriate directories to build, for example: +```shell +# frameworks/base will include base/libs/WindowManager/Shell and base/packages/SystemUI +aidegen frameworks/base \ + vendor/<oem>/packages/SystemUI \ + ... +``` + +## Other useful links +- [go/o-o-summit-20](go/o-o-summit-20) (Video presentations from the WM team) +- [go/o-o-summit-21](go/o-o-summit-21)
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md new file mode 100644 index 000000000000..0dd50b1bee68 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/sysui.md @@ -0,0 +1,65 @@ +# Shell & SystemUI + +--- + +## Setup + +The SystemUI of various products depend on and build against the WM Shell library. To ensure +that we don't inadvertently build dependencies between the Shell library and one particular +product (ie. handheld SysUI), we deliberately separate the initialization of the WM Shell +component from the SysUI component when set up through Dagger. + +**TLDR** Initialize everything as needed in the WM component scope and export only well +defined interfaces to SysUI. + +## Initialization + +There are more details in the Dagger docs, but the general overview of the SysUI/Shell +initialization flow is such: + +1) SysUI Global scope is initialize (see `GlobalModule` and its included modules) +2) WM Shell scope is initialized, for example + 1) On phones: `WMComponent` includes `WMShellModule` which includes `WMShellBaseModule` + (common to all SysUI) + 2) On TVs: `TvWMComponent` includes `TvWMShellModule` which includes `WMShellBaseModule` + 3) etc. +3) SysUI explicitly passes interfaces provided from the `WMComponent` to `SysUIComponent` via + the `SysUIComponent#Builder`, then builds the SysUI scoped components +4) `WMShell` is the SystemUI “service” (in the SysUI scope) that initializes with the app after the +SystemUI part of the dependency graph has been created. It contains the binding code between the +interfaces provided by the Shell and the rest of SystemUI. +5) SysUI can inject the interfaces into its own components + +More detail can be found in [go/wm-sysui-dagger](http://go/wm-sysui-dagger). + +## Interfaces to Shell components + +Within the same process, the WM Shell components can be running on a different thread than the main +SysUI thread (disabled on certain products). This introduces challenges where we have to be +careful about how SysUI calls into the Shell and vice versa. + +As a result, we enforce explicit interfaces between SysUI and Shell components, and the +implementations of the interfaces on each side need to post to the right thread before it calls +into other code. + +For example, you might have: +1) (Shell) ShellFeature interface to be used from SysUI +2) (Shell) ShellFeatureController handles logic, implements ShellFeature interface and posts to + main Shell thread +3) SysUI application init injects Optional<ShellFeature> as an interface to SysUI to call +4) (SysUI) SysUIFeature depends on ShellFeature interface +5) (SysUI) SysUIFeature injects Optional<ShellFeature>, and sets up a callback for the Shell to + call, and the callback posts to the main SysUI thread + +Adding an interface to a Shell component may seem like a lot of boiler plate, but is currently +necessary to maintain proper threading and logic isolation. + +## Configuration changes & other SysUI events + +Aside from direct calls into Shell controllers for exposed features, the Shell also receives +common event callbacks from SysUI via the `ShellController`. This includes things like: + +- Configuration changes +- TODO: Shell init +- TODO: Shell command +- TODO: Keyguard events
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/testing.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/testing.md new file mode 100644 index 000000000000..8a80333facc4 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/testing.md @@ -0,0 +1,49 @@ +# Testing + +--- + +## Unit tests + +New WM Shell unit tests can be added to the +[Shell/tests/unittest](frameworks/base/libs/WindowManager/Shell/tests/unittest) directory, and can +be run via command line using `atest`: +```shell +atest WMShellUnitTests +``` + +If you use the SysUI Studio project, you can run and debug tests directly in the source files +(click on the little arrows next to the test class or test method). + +These unit tests are run as a part of WindowManager presubmit, and the dashboards for these unit +tests tests can be found at [go/wm-tests](http://go/wm-tests). + +This [GCL file](http://go/wm-unit-tests-gcl) configures the tests being run on the server. + +## Flicker tests + +Flicker tests are tests that perform actions and make assertions on the state in Window Manager +and SurfaceFlinger traces captured during the run. + +New WM Shell Flicker tests can be added to the +[Shell/tests/flicker](frameworks/base/libs/WindowManager/Shell/tests/flicker) directory, and can +be run via command line using `atest`: +```shell +atest WMShellFlickerTests +``` + +**Note**: Currently Flicker tests can only be run from the commandline and not via SysUI Studio + +A subset of the flicker tests tests are run as a part of WindowManager presubmit, and the +dashboards for these tests tests can be found at [go/wm-tests-flicker](http://go/wm-tests-flicker). + +## CTS tests + +Some windowing features also have CTS tests to ensure consistent behavior across OEMs. For example: +- Picture-in-Picture: + [PinnedStackTests](cts/tests/framework/base/windowmanager/src/android/server/wm/PinnedStackTests.java) +- etc. + +These can also be run via commandline only using `atest`, for example: +```shell +atest PinnedStackTests +```
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md new file mode 100644 index 000000000000..eac748894432 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/docs/threading.md @@ -0,0 +1,83 @@ +# Threading + +--- + +## Boundaries + +```text + Thread boundary + | + WM Shell | SystemUI + | + | +FeatureController <-> FeatureInterface <--|--> WMShell <-> SysUI + | (^post to shell thread) | (^post to main thread) + ... | + | | + OtherControllers | +``` + +## Threads + +We currently have multiple threads in use in the Shell library depending on the configuration by +the product. +- SysUI main thread (standard main thread) +- `ShellMainThread` (only used if the resource `config_enableShellMainThread` is set true + (ie. phones)) + - This falls back to the SysUI main thread otherwise + - **Note**: + - This thread runs with `THREAD_PRIORITY_DISPLAY` priority since so many windowing-critical + components depend on it + - This is also the UI thread for almost all UI created by the Shell + - The Shell main thread Handler (and the Executor that wraps it) is async, so + messages/runnables used via this Handler are handled immediately if there is no sync + messages prior to it in the queue. +- `ShellBackgroundThread` (for longer running tasks where we don't want to block the shell main + thread) + - This is always another thread even if config_enableShellMainThread is not set true + - **Note**: + - This thread runs with `THREAD_PRIORITY_BACKGROUND` priority +- `ShellAnimationThread` (currently only used for Transitions and Splitscreen, but potentially all + animations could be offloaded here) +- `ShellSplashScreenThread` (only for use with splashscreens) + +## Dagger setup + +The threading-related components are provided by the [WMShellConcurrencyModule](frameworks/base/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellConcurrencyModule.java), +for example, the Executors and Handlers for the various threads that are used. You can request +an executor of the necessary type by using the appropriate annotation for each of the threads (ie. +`@ShellMainThread Executor`) when injecting into your Shell component. + +To get the SysUI main thread, you can use the `@Main` annotation. + +## Best practices + +### Components +- Don't do initialization in the Shell component constructors + - If the host SysUI is not careful, it may construct the WMComponent dependencies on the main + thread, and this reduces the likelihood that components will intiailize on the wrong thread + in such cases +- Be careful of using CountDownLatch and other blocking synchronization mechanisms in Shell code + - If the Shell main thread is not a separate thread, this will cause a deadlock +- Callbacks, Observers, Listeners to any non-shell component should post onto main Shell thread + - This includes Binder calls, SysUI calls, BroadcastReceivers, etc. Basically any API that + takes a runnable should either be registered with the right Executor/Handler or posted to + the main Shell thread manually +- Since everything in the Shell runs on the main Shell thread, you do **not** need to explicitly + `synchronize` your code (unless you are trying to prevent reentrantcy, but that can also be + done in other ways) + +### Handlers/Executors +- You generally **never** need to create Handlers explicitly, instead inject `@ShellMainThread + ShellExecutor` instead + - This is a common pattern to defer logic in UI code, but the Handler created wraps the Looper + that is currently running, which can be wrong (see above for initialization vs construction) +- That said, sometimes Handlers are necessary because Framework API only takes Handlers or you + want to dedupe multiple messages + - In such cases inject `@ShellMainThread Handler` or use view.getHandler() which should be OK + assuming that the view root was initialized on the main Shell thread +- **Never use Looper.getMainLooper()** + - It's likely going to be wrong, you can inject `@Main ShellExecutor` to get the SysUI main thread + +### Testing +- You can use a `TestShellExecutor` to control the processing of messages
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index 95de2dc61a43..c5df53b6dbc8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -60,6 +60,8 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.sysui.ConfigurationChangeListener; +import com.android.wm.shell.sysui.ShellController; import java.util.ArrayList; import java.util.Optional; @@ -68,21 +70,20 @@ import java.util.Optional; * Handles the global drag and drop handling for the Shell. */ public class DragAndDropController implements DisplayController.OnDisplaysChangedListener, - View.OnDragListener { + View.OnDragListener, ConfigurationChangeListener { private static final String TAG = DragAndDropController.class.getSimpleName(); private final Context mContext; + private final ShellController mShellController; private final DisplayController mDisplayController; private final DragAndDropEventLogger mLogger; private final IconProvider mIconProvider; private SplitScreenController mSplitScreen; private ShellExecutor mMainExecutor; - private DragAndDropImpl mImpl; private ArrayList<DragAndDropListener> mListeners = new ArrayList<>(); private final SparseArray<PerDisplay> mDisplayDropTargets = new SparseArray<>(); - private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction(); /** * Listener called during drag events, currently just onDragStarted. @@ -92,23 +93,24 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange void onDragStarted(); } - public DragAndDropController(Context context, DisplayController displayController, - UiEventLogger uiEventLogger, IconProvider iconProvider, ShellExecutor mainExecutor) { + public DragAndDropController(Context context, + ShellController shellController, + DisplayController displayController, + UiEventLogger uiEventLogger, + IconProvider iconProvider, + ShellExecutor mainExecutor) { mContext = context; + mShellController = shellController; mDisplayController = displayController; mLogger = new DragAndDropEventLogger(uiEventLogger); mIconProvider = iconProvider; mMainExecutor = mainExecutor; - mImpl = new DragAndDropImpl(); - } - - public DragAndDrop asDragAndDrop() { - return mImpl; } public void initialize(Optional<SplitScreenController> splitscreen) { mSplitScreen = splitscreen.orElse(null); mDisplayController.addDisplayWindowListener(this); + mShellController.addConfigurationChangeListener(this); } /** Adds a listener to be notified of drag and drop events. */ @@ -310,13 +312,15 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange return mimeTypes; } - private void onThemeChange() { + @Override + public void onThemeChanged() { for (int i = 0; i < mDisplayDropTargets.size(); i++) { mDisplayDropTargets.get(i).dragLayout.onThemeChange(); } } - private void onConfigChanged(Configuration newConfig) { + @Override + public void onConfigurationChanged(Configuration newConfig) { for (int i = 0; i < mDisplayDropTargets.size(); i++) { mDisplayDropTargets.get(i).dragLayout.onConfigChanged(newConfig); } @@ -342,21 +346,4 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange dragLayout = dl; } } - - private class DragAndDropImpl implements DragAndDrop { - - @Override - public void onThemeChanged() { - mMainExecutor.execute(() -> { - DragAndDropController.this.onThemeChange(); - }); - } - - @Override - public void onConfigChanged(Configuration newConfig) { - mMainExecutor.execute(() -> { - DragAndDropController.this.onConfigChanged(newConfig); - }); - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java index 6373728344a4..435d8eaa563e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java @@ -45,12 +45,10 @@ import android.app.WindowConfiguration; import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.ClipDescription; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.LauncherApps; -import android.content.pm.ResolveInfo; import android.graphics.Insets; import android.graphics.Rect; import android.os.Bundle; @@ -64,19 +62,15 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.logging.InstanceId; -import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayLayout; -import com.android.wm.shell.common.split.SplitLayout; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; -import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; -import java.util.Objects; /** * The policy for handling drag and drop operations to shell. @@ -269,47 +263,11 @@ public class DragAndDropPolicy { mStarter.startShortcut(packageName, id, position, opts, user); } else { final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT); - mStarter.startIntent(launchIntent, getStartIntentFillInIntent(launchIntent, position), - position, opts); + mStarter.startIntent(launchIntent, null /* fillIntent */, position, opts); } } /** - * Returns the fill-in intent to use when starting an app from a drop. - */ - @VisibleForTesting - Intent getStartIntentFillInIntent(PendingIntent launchIntent, @SplitPosition int position) { - // Get the drag app - final List<ResolveInfo> infos = launchIntent.queryIntentComponents(0 /* flags */); - final ComponentName dragIntentActivity = !infos.isEmpty() - ? infos.get(0).activityInfo.getComponentName() - : null; - - // Get the current app (either fullscreen or the remaining app post-drop if in splitscreen) - final boolean inSplitScreen = mSplitScreen != null - && mSplitScreen.isSplitScreenVisible(); - final ComponentName currentActivity; - if (!inSplitScreen) { - currentActivity = mSession.runningTaskInfo != null - ? mSession.runningTaskInfo.baseIntent.getComponent() - : null; - } else { - final ActivityManager.RunningTaskInfo nonReplacedTaskInfo = - mSplitScreen.getTaskInfo(SplitLayout.reversePosition(position)); - currentActivity = nonReplacedTaskInfo.baseIntent.getComponent(); - } - - if (Objects.equals(currentActivity, dragIntentActivity)) { - // Only apply MULTIPLE_TASK if we are dragging the same activity - final Intent fillInIntent = new Intent(); - fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DRAG_AND_DROP, "Adding MULTIPLE_TASK"); - return fillInIntent; - } - return null; - } - - /** * Per-drag session data. */ private static class DragSession { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java index 23f76ca5f6ae..665b035bc41c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutController.java @@ -19,7 +19,6 @@ package com.android.wm.shell.hidedisplaycutout; import android.content.Context; import android.content.res.Configuration; import android.os.SystemProperties; -import android.util.Slog; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -27,20 +26,20 @@ import androidx.annotation.VisibleForTesting; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.sysui.ConfigurationChangeListener; +import com.android.wm.shell.sysui.ShellController; import java.io.PrintWriter; -import java.util.concurrent.TimeUnit; /** * Manages the hide display cutout status. */ -public class HideDisplayCutoutController { +public class HideDisplayCutoutController implements ConfigurationChangeListener { private static final String TAG = "HideDisplayCutoutController"; private final Context mContext; + private final ShellController mShellController; private final HideDisplayCutoutOrganizer mOrganizer; - private final ShellExecutor mMainExecutor; - private final HideDisplayCutoutImpl mImpl = new HideDisplayCutoutImpl(); @VisibleForTesting boolean mEnabled; @@ -49,8 +48,9 @@ public class HideDisplayCutoutController { * supported. */ @Nullable - public static HideDisplayCutoutController create( - Context context, DisplayController displayController, ShellExecutor mainExecutor) { + public static HideDisplayCutoutController create(Context context, + ShellController shellController, DisplayController displayController, + ShellExecutor mainExecutor) { // The SystemProperty is set for devices that support this feature and is used to control // whether to create the HideDisplayCutout instance. // It's defined in the device.mk (e.g. device/google/crosshatch/device.mk). @@ -60,19 +60,16 @@ public class HideDisplayCutoutController { HideDisplayCutoutOrganizer organizer = new HideDisplayCutoutOrganizer(context, displayController, mainExecutor); - return new HideDisplayCutoutController(context, organizer, mainExecutor); + return new HideDisplayCutoutController(context, shellController, organizer); } - HideDisplayCutoutController(Context context, HideDisplayCutoutOrganizer organizer, - ShellExecutor mainExecutor) { + HideDisplayCutoutController(Context context, ShellController shellController, + HideDisplayCutoutOrganizer organizer) { mContext = context; + mShellController = shellController; mOrganizer = organizer; - mMainExecutor = mainExecutor; updateStatus(); - } - - public HideDisplayCutout asHideDisplayCutout() { - return mImpl; + mShellController.addConfigurationChangeListener(this); } @VisibleForTesting @@ -94,7 +91,8 @@ public class HideDisplayCutoutController { } } - private void onConfigurationChanged(Configuration newConfig) { + @Override + public void onConfigurationChanged(Configuration newConfig) { updateStatus(); } @@ -107,13 +105,4 @@ public class HideDisplayCutoutController { pw.println(mEnabled); mOrganizer.dump(pw); } - - private class HideDisplayCutoutImpl implements HideDisplayCutout { - @Override - public void onConfigurationChanged(Configuration newConfig) { - mMainExecutor.execute(() -> { - HideDisplayCutoutController.this.onConfigurationChanged(newConfig); - }); - } - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java index 3f7d78dda037..9478b347653f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizer.java @@ -128,9 +128,10 @@ class HideDisplayCutoutOrganizer extends DisplayAreaOrganizer { final WindowContainerTransaction wct = new WindowContainerTransaction(); final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - applyBoundsAndOffsets( - displayAreaInfo.token, mDisplayAreaMap.get(displayAreaInfo.token), wct, t); + final SurfaceControl leash = mDisplayAreaMap.get(displayAreaInfo.token); + applyBoundsAndOffsets(displayAreaInfo.token, leash, wct, t); applyTransaction(wct, t); + leash.release(); mDisplayAreaMap.remove(displayAreaInfo.token); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java index b00182f36cc8..76c0f41997ad 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java @@ -16,7 +16,6 @@ package com.android.wm.shell.onehanded; -import android.content.res.Configuration; import android.os.SystemProperties; import com.android.wm.shell.common.annotations.ExternalThread; @@ -83,17 +82,7 @@ public interface OneHanded { void registerTransitionCallback(OneHandedTransitionCallback callback); /** - * Receive onConfigurationChanged() events - */ - void onConfigChanged(Configuration newConfig); - - /** * Notifies when user switch complete */ void onUserSwitch(int userId); - - /** - * Notifies when keyguard visibility changed - */ - void onKeyguardVisibilityChanged(boolean showing); } 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 1d8ac2b576e9..24f02ac1a6cf 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 @@ -28,12 +28,10 @@ import static com.android.wm.shell.onehanded.OneHandedState.STATE_NONE; import android.annotation.BinderThread; import android.content.ComponentName; import android.content.Context; -import android.content.om.IOverlayManager; import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.Rect; import android.os.Handler; -import android.os.ServiceManager; import android.os.SystemProperties; import android.provider.Settings; import android.util.Slog; @@ -56,6 +54,9 @@ import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.common.annotations.ExternalThread; +import com.android.wm.shell.sysui.ConfigurationChangeListener; +import com.android.wm.shell.sysui.KeyguardChangeListener; +import com.android.wm.shell.sysui.ShellController; import java.io.PrintWriter; @@ -63,7 +64,8 @@ import java.io.PrintWriter; * Manages and manipulates the one handed states, transitions, and gesture for phones. */ public class OneHandedController implements RemoteCallable<OneHandedController>, - DisplayChangeController.OnDisplayChangingListener { + DisplayChangeController.OnDisplayChangingListener, ConfigurationChangeListener, + KeyguardChangeListener { private static final String TAG = "OneHandedController"; private static final String ONE_HANDED_MODE_OFFSET_PERCENTAGE = @@ -83,6 +85,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, private Context mContext; + private final ShellController mShellController; private final AccessibilityManager mAccessibilityManager; private final DisplayController mDisplayController; private final OneHandedSettingsUtil mOneHandedSettingsUtil; @@ -92,7 +95,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, private final OneHandedState mState; private final OneHandedTutorialHandler mTutorialHandler; private final TaskStackListenerImpl mTaskStackListener; - private final IOverlayManager mOverlayManager; private final ShellExecutor mMainExecutor; private final Handler mMainHandler; private final OneHandedImpl mImpl = new OneHandedImpl(); @@ -191,8 +193,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, * Creates {@link OneHandedController}, returns {@code null} if the feature is not supported. */ public static OneHandedController create( - Context context, WindowManager windowManager, DisplayController displayController, - DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener, + Context context, ShellController shellController, WindowManager windowManager, + DisplayController displayController, DisplayLayout displayLayout, + TaskStackListenerImpl taskStackListener, InteractionJankMonitor jankMonitor, UiEventLogger uiEventLogger, ShellExecutor mainExecutor, Handler mainHandler) { OneHandedSettingsUtil settingsUtil = new OneHandedSettingsUtil(); @@ -210,16 +213,15 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, context, displayLayout, settingsUtil, animationController, tutorialHandler, jankMonitor, mainExecutor); OneHandedUiEventLogger oneHandedUiEventsLogger = new OneHandedUiEventLogger(uiEventLogger); - IOverlayManager overlayManager = IOverlayManager.Stub.asInterface( - ServiceManager.getService(Context.OVERLAY_SERVICE)); - return new OneHandedController(context, displayController, organizer, touchHandler, - tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler, oneHandedState, - jankMonitor, oneHandedUiEventsLogger, overlayManager, taskStackListener, + return new OneHandedController(context, shellController, displayController, organizer, + touchHandler, tutorialHandler, settingsUtil, accessibilityUtil, timeoutHandler, + oneHandedState, oneHandedUiEventsLogger, taskStackListener, mainExecutor, mainHandler); } @VisibleForTesting OneHandedController(Context context, + ShellController shellController, DisplayController displayController, OneHandedDisplayAreaOrganizer displayAreaOrganizer, OneHandedTouchHandler touchHandler, @@ -228,13 +230,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, OneHandedAccessibilityUtil oneHandedAccessibilityUtil, OneHandedTimeoutHandler timeoutHandler, OneHandedState state, - InteractionJankMonitor jankMonitor, OneHandedUiEventLogger uiEventsLogger, - IOverlayManager overlayManager, TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor, Handler mainHandler) { mContext = context; + mShellController = shellController; mOneHandedSettingsUtil = settingsUtil; mOneHandedAccessibilityUtil = oneHandedAccessibilityUtil; mDisplayAreaOrganizer = displayAreaOrganizer; @@ -242,7 +243,6 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mTouchHandler = touchHandler; mState = state; mTutorialHandler = tutorialHandler; - mOverlayManager = overlayManager; mMainExecutor = mainExecutor; mMainHandler = mainHandler; mOneHandedUiEventLogger = uiEventsLogger; @@ -280,6 +280,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mAccessibilityStateChangeListener); mState.addSListeners(mTutorialHandler); + mShellController.addConfigurationChangeListener(this); + mShellController.addKeyguardChangeListener(this); } public OneHanded asOneHanded() { @@ -595,7 +597,8 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mLockedDisabled = locked && !enabled; } - private void onConfigChanged(Configuration newConfig) { + @Override + public void onConfigurationChanged(Configuration newConfig) { if (mTutorialHandler == null) { return; } @@ -605,8 +608,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, mTutorialHandler.onConfigurationChanged(); } - private void onKeyguardVisibilityChanged(boolean showing) { - mKeyguardShowing = showing; + @Override + public void onKeyguardVisibilityChanged(boolean visible, boolean occluded, + boolean animatingDismiss) { + mKeyguardShowing = visible; + stopOneHanded(); } private void onUserSwitch(int newUserId) { @@ -675,9 +681,12 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, return; } + if (mState.getState() == STATE_ACTIVE) { + mOneHandedUiEventLogger.writeEvent( + OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT); + } + mDisplayAreaOrganizer.onRotateDisplay(mContext, toRotation, wct); - mOneHandedUiEventLogger.writeEvent( - OneHandedUiEventLogger.EVENT_ONE_HANDED_TRIGGER_ROTATION_OUT); } /** @@ -751,25 +760,11 @@ public class OneHandedController implements RemoteCallable<OneHandedController>, } @Override - public void onConfigChanged(Configuration newConfig) { - mMainExecutor.execute(() -> { - OneHandedController.this.onConfigChanged(newConfig); - }); - } - - @Override public void onUserSwitch(int userId) { mMainExecutor.execute(() -> { OneHandedController.this.onUserSwitch(userId); }); } - - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - mMainExecutor.execute(() -> { - OneHandedController.this.onKeyguardVisibilityChanged(showing); - }); - } } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index f61d1b95bd85..451afa08040c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -159,6 +159,10 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @Override public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) { + final SurfaceControl leash = mDisplayAreaTokenMap.get(displayAreaInfo.token); + if (leash != null) { + leash.release(); + } mDisplayAreaTokenMap.remove(displayAreaInfo.token); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index bbc47e47afc5..38631ce26cd1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -16,7 +16,6 @@ package com.android.wm.shell.pip; -import android.content.res.Configuration; import android.graphics.Rect; import com.android.wm.shell.common.annotations.ExternalThread; @@ -44,24 +43,6 @@ public interface Pip { } /** - * Called when configuration is changed. - */ - default void onConfigurationChanged(Configuration newConfig) { - } - - /** - * Called when display size or font size of settings changed - */ - default void onDensityOrFontScaleChanged() { - } - - /** - * Called when overlay package change invoked. - */ - default void onOverlayChanged() { - } - - /** * Called when SysUI state changed. * * @param isSysUiStateValid Is SysUI state valid or not. @@ -120,23 +101,6 @@ public interface Pip { default void removePipExclusionBoundsChangeListener(Consumer<Rect> listener) { } /** - * Called when the visibility of keyguard is changed. - * @param showing {@code true} if keyguard is now showing, {@code false} otherwise. - * @param animating {@code true} if system is animating between keyguard and surface behind, - * this only makes sense when showing is {@code false}. - */ - default void onKeyguardVisibilityChanged(boolean showing, boolean animating) { } - - /** - * Called when the dismissing animation keyguard and surfaces behind is finished. - * See also {@link #onKeyguardVisibilityChanged(boolean, boolean)}. - * - * TODO(b/206741900) deprecate this path once we're able to animate the PiP window as part of - * keyguard dismiss animation. - */ - default void onKeyguardDismissAnimationFinished() { } - - /** * Dump the current state and information if need. * * @param pw The stream to dump information to. 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 22b0ccbc8488..da88c2de6c01 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 @@ -943,6 +943,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mPipBoundsState.setBounds(new Rect()); mPipUiEventLoggerLogger.setTaskInfo(null); mMainExecutor.executeDelayed(() -> mPipMenuController.detach(), 0); + mLeash = null; if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) { mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY); 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 05a890fc65ed..51be2a534dd7 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 @@ -315,7 +315,7 @@ public class PipTransition extends PipTransitionController { } @Override - public void onTransitionMerged(@NonNull IBinder transition) { + public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) { if (transition != mExitTransition) { return; } @@ -328,7 +328,7 @@ public class PipTransition extends PipTransitionController { } // Unset exitTransition AFTER cancel so that finishResize knows we are merging. mExitTransition = null; - if (!cancelled) return; + if (!cancelled || aborted) return; final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo(); if (taskInfo != null) { startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(), 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 3000998f210d..420d6067f420 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 @@ -87,6 +87,9 @@ import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipTransitionState; import com.android.wm.shell.pip.PipUtils; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.sysui.ConfigurationChangeListener; +import com.android.wm.shell.sysui.KeyguardChangeListener; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; @@ -100,7 +103,7 @@ import java.util.function.Consumer; * Manages the picture-in-picture (PIP) UI and states for Phones. */ public class PipController implements PipTransitionController.PipTransitionCallback, - RemoteCallable<PipController> { + RemoteCallable<PipController>, ConfigurationChangeListener, KeyguardChangeListener { private static final String TAG = "PipController"; private Context mContext; @@ -119,6 +122,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb private TaskStackListenerImpl mTaskStackListener; private PipParamsChangedForwarder mPipParamsChangedForwarder; private Optional<OneHandedController> mOneHandedController; + private final ShellController mShellController; protected final PipImpl mImpl; private final Rect mTmpInsetBounds = new Rect(); @@ -290,13 +294,20 @@ public class PipController implements PipTransitionController.PipTransitionCallb * Instantiates {@link PipController}, returns {@code null} if the feature not supported. */ @Nullable - public static Pip create(Context context, DisplayController displayController, - PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, - PipKeepClearAlgorithm pipKeepClearAlgorithm, PipBoundsState pipBoundsState, - PipMotionHelper pipMotionHelper, PipMediaController pipMediaController, - PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, + public static Pip create(Context context, + ShellController shellController, + DisplayController displayController, + PipAppOpsListener pipAppOpsListener, + PipBoundsAlgorithm pipBoundsAlgorithm, + PipKeepClearAlgorithm pipKeepClearAlgorithm, + PipBoundsState pipBoundsState, + PipMotionHelper pipMotionHelper, + PipMediaController pipMediaController, + PhonePipMenuController phonePipMenuController, + PipTaskOrganizer pipTaskOrganizer, PipTransitionState pipTransitionState, - PipTouchHandler pipTouchHandler, PipTransitionController pipTransitionController, + PipTouchHandler pipTouchHandler, + PipTransitionController pipTransitionController, WindowManagerShellWrapper windowManagerShellWrapper, TaskStackListenerImpl taskStackListener, PipParamsChangedForwarder pipParamsChangedForwarder, @@ -308,9 +319,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb return null; } - return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, - pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController, - phonePipMenuController, pipTaskOrganizer, pipTransitionState, + return new PipController(context, shellController, displayController, pipAppOpsListener, + pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, + pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController, windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder, oneHandedController, mainExecutor) @@ -318,6 +329,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb } protected PipController(Context context, + ShellController shellController, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, @@ -343,6 +355,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb } mContext = context; + mShellController = shellController; mImpl = new PipImpl(); mWindowManagerShellWrapper = windowManagerShellWrapper; mDisplayController = displayController; @@ -513,6 +526,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb } }); }); + + mShellController.addConfigurationChangeListener(this); + mShellController.addKeyguardChangeListener(this); } @Override @@ -525,18 +541,21 @@ public class PipController implements PipTransitionController.PipTransitionCallb return mMainExecutor; } - private void onConfigurationChanged(Configuration newConfig) { + @Override + public void onConfigurationChanged(Configuration newConfig) { mPipBoundsAlgorithm.onConfigurationChanged(mContext); mTouchHandler.onConfigurationChanged(); mPipBoundsState.onConfigurationChanged(); } - private void onDensityOrFontScaleChanged() { + @Override + public void onDensityOrFontScaleChanged() { mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext); onPipResourceDimensionsChanged(); } - private void onOverlayChanged() { + @Override + public void onThemeChanged() { mTouchHandler.onOverlayChanged(); onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()), false /* saveRestoreSnapFraction */); @@ -644,21 +663,24 @@ public class PipController implements PipTransitionController.PipTransitionCallb * finished first to reset the visibility of PiP window. * See also {@link #onKeyguardDismissAnimationFinished()} */ - private void onKeyguardVisibilityChanged(boolean keyguardShowing, boolean animating) { + @Override + public void onKeyguardVisibilityChanged(boolean visible, boolean occluded, + boolean animatingDismiss) { if (!mPipTaskOrganizer.isInPip()) { return; } - if (keyguardShowing) { + if (visible) { mIsKeyguardShowingOrAnimating = true; hidePipMenu(null /* onStartCallback */, null /* onEndCallback */); mPipTaskOrganizer.setPipVisibility(false); - } else if (!animating) { + } else if (!animatingDismiss) { mIsKeyguardShowingOrAnimating = false; mPipTaskOrganizer.setPipVisibility(true); } } - private void onKeyguardDismissAnimationFinished() { + @Override + public void onKeyguardDismissAnimationFinished() { if (mPipTaskOrganizer.isInPip()) { mIsKeyguardShowingOrAnimating = false; mPipTaskOrganizer.setPipVisibility(true); @@ -923,27 +945,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override - public void onConfigurationChanged(Configuration newConfig) { - mMainExecutor.execute(() -> { - PipController.this.onConfigurationChanged(newConfig); - }); - } - - @Override - public void onDensityOrFontScaleChanged() { - mMainExecutor.execute(() -> { - PipController.this.onDensityOrFontScaleChanged(); - }); - } - - @Override - public void onOverlayChanged() { - mMainExecutor.execute(() -> { - PipController.this.onOverlayChanged(); - }); - } - - @Override public void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) { mMainExecutor.execute(() -> { PipController.this.onSystemUiStateChanged(isSysUiStateValid, flag); @@ -1000,18 +1001,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb } @Override - public void onKeyguardVisibilityChanged(boolean showing, boolean animating) { - mMainExecutor.execute(() -> { - PipController.this.onKeyguardVisibilityChanged(showing, animating); - }); - } - - @Override - public void onKeyguardDismissAnimationFinished() { - mMainExecutor.execute(PipController.this::onKeyguardDismissAnimationFinished); - } - - @Override public void dump(PrintWriter pw) { try { mMainExecutor.executeBlocking(() -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index fa48def9c7d7..a24d9618032d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -49,6 +49,8 @@ import com.android.wm.shell.pip.PipParamsChangedForwarder; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import com.android.wm.shell.sysui.ConfigurationChangeListener; +import com.android.wm.shell.sysui.ShellController; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -61,7 +63,8 @@ import java.util.Set; */ public class TvPipController implements PipTransitionController.PipTransitionCallback, TvPipBoundsController.PipBoundsListener, TvPipMenuController.Delegate, - TvPipNotificationController.Delegate, DisplayController.OnDisplaysChangedListener { + TvPipNotificationController.Delegate, DisplayController.OnDisplaysChangedListener, + ConfigurationChangeListener { private static final String TAG = "TvPipController"; static final boolean DEBUG = false; @@ -93,6 +96,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal private final Context mContext; + private final ShellController mShellController; private final TvPipBoundsState mTvPipBoundsState; private final TvPipBoundsAlgorithm mTvPipBoundsAlgorithm; private final TvPipBoundsController mTvPipBoundsController; @@ -117,6 +121,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal public static Pip create( Context context, + ShellController shellController, TvPipBoundsState tvPipBoundsState, TvPipBoundsAlgorithm tvPipBoundsAlgorithm, TvPipBoundsController tvPipBoundsController, @@ -133,6 +138,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal ShellExecutor mainExecutor) { return new TvPipController( context, + shellController, tvPipBoundsState, tvPipBoundsAlgorithm, tvPipBoundsController, @@ -151,6 +157,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal private TvPipController( Context context, + ShellController shellController, TvPipBoundsState tvPipBoundsState, TvPipBoundsAlgorithm tvPipBoundsAlgorithm, TvPipBoundsController tvPipBoundsController, @@ -167,6 +174,7 @@ public class TvPipController implements PipTransitionController.PipTransitionCal ShellExecutor mainExecutor) { mContext = context; mMainExecutor = mainExecutor; + mShellController = shellController; mTvPipBoundsState = tvPipBoundsState; mTvPipBoundsState.setDisplayId(context.getDisplayId()); @@ -193,9 +201,12 @@ public class TvPipController implements PipTransitionController.PipTransitionCal registerTaskStackListenerCallback(taskStackListener); registerWmShellPinnedStackListener(wmShell); displayController.addDisplayWindowListener(this); + + mShellController.addConfigurationChangeListener(this); } - private void onConfigurationChanged(Configuration newConfig) { + @Override + public void onConfigurationChanged(Configuration newConfig) { if (DEBUG) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: onConfigurationChanged(), state=%s", TAG, stateToName(mState)); @@ -669,13 +680,6 @@ public class TvPipController implements PipTransitionController.PipTransitionCal private class TvPipImpl implements Pip { @Override - public void onConfigurationChanged(Configuration newConfig) { - mMainExecutor.execute(() -> { - TvPipController.this.onConfigurationChanged(newConfig); - }); - } - - @Override public void registerSessionListenerForCurrentUser() { mMainExecutor.execute(() -> { TvPipController.this.registerSessionListenerForCurrentUser(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java index d04c34916256..b2961518b66a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -26,6 +26,8 @@ import com.android.internal.protolog.common.IProtoLogGroup; public enum ShellProtoLogGroup implements IProtoLogGroup { // NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict // with those in the framework ProtoLogGroup + WM_SHELL_INIT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, + Consts.TAG_WM_SHELL), WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, @@ -38,10 +40,12 @@ public enum ShellProtoLogGroup implements IProtoLogGroup { "ShellBackPreview"), WM_SHELL_RECENT_TASKS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), - WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, - false, Consts.TAG_WM_SHELL), + WM_SHELL_PICTURE_IN_PICTURE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM_SHELL), WM_SHELL_SPLIT_SCREEN(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM_SHELL), + WM_SHELL_SYSUI_EVENTS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + Consts.TAG_WM_SHELL), TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest"); private final boolean mEnabled; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java index 2bfa5db502ce..b0080b24c609 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java @@ -16,6 +16,9 @@ package com.android.wm.shell.splitscreen; +import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES; +import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES; + import android.content.Context; import android.view.SurfaceSession; import android.window.WindowContainerToken; @@ -42,6 +45,11 @@ class MainStage extends StageTaskListener { iconProvider); } + @Override + void dismiss(WindowContainerTransaction wct, boolean toTop) { + deactivate(wct, toTop); + } + boolean isActive() { return mIsActive; } @@ -74,10 +82,10 @@ class MainStage extends StageTaskListener { if (mRootTaskInfo == null) return; final WindowContainerToken rootToken = mRootTaskInfo.token; wct.reparentTasks( - rootToken, - null /* newParent */, - CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, - CONTROLLED_ACTIVITY_TYPES, - toTop); + rootToken, + null /* newParent */, + null /* windowingModes */, + null /* activityTypes */, + toTop); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java index f92a0d3901b9..86efbe0af79c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java @@ -42,13 +42,18 @@ class SideStage extends StageTaskListener { iconProvider); } + @Override + void dismiss(WindowContainerTransaction wct, boolean toTop) { + removeAllTasks(wct, toTop); + } + boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) { if (mChildrenTaskInfo.size() == 0) return false; wct.reparentTasks( mRootTaskInfo.token, null /* newParent */, - CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, - CONTROLLED_ACTIVITY_TYPES, + null /* windowingModes */, + null /* activityTypes */, toTop); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java index 29b6311e5041..e73b799b7a3d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java @@ -77,12 +77,6 @@ public interface SplitScreen { return null; } - /** - * Called when the visibility of the keyguard changes. - * @param showing Indicates if the keyguard is now visible. - */ - void onKeyguardVisibilityChanged(boolean showing); - /** Called when device waking up finished. */ void onFinishedWakingUp(); 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 ee493668994d..53ec39d954c4 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 @@ -18,6 +18,7 @@ package com.android.wm.shell.splitscreen; import static android.app.ActivityManager.START_SUCCESS; import static android.app.ActivityManager.START_TASK_TO_FRONT; +import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.RemoteAnimationTarget.MODE_OPENING; @@ -58,7 +59,9 @@ import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; +import com.android.internal.protolog.common.ProtoLog; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.RootTaskDisplayAreaOrganizer; import com.android.wm.shell.ShellTaskOrganizer; @@ -74,8 +77,11 @@ import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.split.SplitLayout; import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition; import com.android.wm.shell.draganddrop.DragAndDropPolicy; +import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.splitscreen.SplitScreen.StageType; +import com.android.wm.shell.sysui.KeyguardChangeListener; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.transition.LegacyTransitions; import com.android.wm.shell.transition.Transitions; @@ -95,18 +101,18 @@ import java.util.concurrent.Executor; */ // TODO(b/198577848): Implement split screen flicker test to consolidate CUJ of split screen. public class SplitScreenController implements DragAndDropPolicy.Starter, - RemoteCallable<SplitScreenController> { + RemoteCallable<SplitScreenController>, KeyguardChangeListener { private static final String TAG = SplitScreenController.class.getSimpleName(); - static final int EXIT_REASON_UNKNOWN = 0; - static final int EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW = 1; - static final int EXIT_REASON_APP_FINISHED = 2; - static final int EXIT_REASON_DEVICE_FOLDED = 3; - static final int EXIT_REASON_DRAG_DIVIDER = 4; - static final int EXIT_REASON_RETURN_HOME = 5; - 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; + public static final int EXIT_REASON_UNKNOWN = 0; + public static final int EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW = 1; + public static final int EXIT_REASON_APP_FINISHED = 2; + public static final int EXIT_REASON_DEVICE_FOLDED = 3; + public static final int EXIT_REASON_DRAG_DIVIDER = 4; + public static final int EXIT_REASON_RETURN_HOME = 5; + public static final int EXIT_REASON_ROOT_TASK_VANISHED = 6; + public static final int EXIT_REASON_SCREEN_LOCKED = 7; + public static final int EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP = 8; public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9; @IntDef(value = { EXIT_REASON_UNKNOWN, @@ -123,6 +129,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, @Retention(RetentionPolicy.SOURCE) @interface ExitReason{} + private final ShellController mShellController; private final ShellTaskOrganizer mTaskOrganizer; private final SyncTransactionQueue mSyncQueue; private final Context mContext; @@ -143,7 +150,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, // outside the bounds of the roots by being reparented into a higher level fullscreen container private SurfaceControl mSplitTasksContainerLayer; - public SplitScreenController(ShellTaskOrganizer shellTaskOrganizer, + public SplitScreenController(ShellController shellController, + ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue, Context context, RootTaskDisplayAreaOrganizer rootTDAOrganizer, ShellExecutor mainExecutor, DisplayController displayController, @@ -151,6 +159,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, DisplayInsetsController displayInsetsController, Transitions transitions, TransactionPool transactionPool, IconProvider iconProvider, Optional<RecentTasksController> recentTasks) { + mShellController = shellController; mTaskOrganizer = shellTaskOrganizer; mSyncQueue = syncQueue; mContext = context; @@ -181,6 +190,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } public void onOrganizerRegistered() { + mShellController.addKeyguardChangeListener(this); if (mStageCoordinator == null) { // TODO: Multi-display mStageCoordinator = new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue, @@ -198,6 +208,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, return mStageCoordinator; } + public ActivityManager.RunningTaskInfo getFocusingTaskInfo() { + return mStageCoordinator.getFocusingTaskInfo(); + } + + public boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) { + return mStageCoordinator.isValidToEnterSplitScreen(taskInfo); + } + @Nullable public ActivityManager.RunningTaskInfo getTaskInfo(@SplitPosition int splitPosition) { if (!isSplitScreenVisible() || splitPosition == SPLIT_POSITION_UNDEFINED) { @@ -271,8 +289,10 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator.exitSplitScreen(toTopTaskId, exitReason); } - public void onKeyguardVisibilityChanged(boolean showing) { - mStageCoordinator.onKeyguardVisibilityChanged(showing); + @Override + public void onKeyguardVisibilityChanged(boolean visible, boolean occluded, + boolean animatingDismiss) { + mStageCoordinator.onKeyguardVisibilityChanged(visible); } public void onFinishedWakingUp() { @@ -341,14 +361,21 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, null /* wct */); - // Flag this as a no-user-action launch to prevent sending user leaving event to the - // current top activity since it's going to be put into another side of the split. This - // prevents the current top activity from going into pip mode due to user leaving event. if (fillInIntent == null) { fillInIntent = new Intent(); } + // Flag this as a no-user-action launch to prevent sending user leaving event to the + // current top activity since it's going to be put into another side of the split. This + // prevents the current top activity from going into pip mode due to user leaving event. fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION); + // Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the + // split. + if (isLaunchingAdjacently(intent.getIntent(), position)) { + fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK"); + } + intent.send(mContext, 0, fillInIntent, null /* onFinished */, null /* handler */, null /* requiredPermission */, options); } catch (PendingIntent.CanceledException e) { @@ -358,6 +385,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, private void startIntentLegacy(PendingIntent intent, @Nullable Intent fillInIntent, @SplitPosition int position, @Nullable Bundle options) { + boolean startSameActivityAdjacently = isLaunchingAdjacently(intent.getIntent(), position); + final WindowContainerTransaction evictWct = new WindowContainerTransaction(); mStageCoordinator.prepareEvictChildTasks(position, evictWct); @@ -368,14 +397,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t) { if (apps == null || apps.length == 0) { - final ActivityManager.RunningTaskInfo pairedTaskInfo = - getTaskInfo(SplitLayout.reversePosition(position)); - final ComponentName pairedActivity = pairedTaskInfo != null - ? pairedTaskInfo.baseIntent.getComponent() : null; - final ComponentName intentActivity = intent.getIntent() != null - ? intent.getIntent().getComponent() : null; - - if (Objects.equals(pairedActivity, intentActivity)) { + if (startSameActivityAdjacently) { // Switch split position if dragging the same activity to another side. setSideStagePosition(SplitLayout.reversePosition( mStageCoordinator.getSideStagePosition())); @@ -417,11 +439,44 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, fillInIntent = new Intent(); } fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION); + if (startSameActivityAdjacently) { + fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK"); + } wct.sendPendingIntent(intent, fillInIntent, options); mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct); } + /** Returns {@code true} if it's launching the same component on both sides of the split. */ + @VisibleForTesting + boolean isLaunchingAdjacently(@Nullable Intent startIntent, + @SplitPosition int position) { + if (startIntent == null) { + return false; + } + + final ComponentName launchingActivity = startIntent.getComponent(); + if (launchingActivity == null) { + return false; + } + + if (isSplitScreenVisible()) { + final ActivityManager.RunningTaskInfo pairedTaskInfo = + getTaskInfo(SplitLayout.reversePosition(position)); + final ComponentName pairedActivity = pairedTaskInfo != null + ? pairedTaskInfo.baseIntent.getComponent() : null; + return Objects.equals(launchingActivity, pairedActivity); + } + + final ActivityManager.RunningTaskInfo taskInfo = getFocusingTaskInfo(); + if (taskInfo != null && isValidToEnterSplitScreen(taskInfo)) { + return Objects.equals(taskInfo.baseIntent.getComponent(), launchingActivity); + } + + return false; + } + RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) { if (isSplitScreenVisible()) { // Evict child tasks except the top visible one under split root to ensure it could be @@ -434,7 +489,15 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) { - return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/); + try { + return reparentSplitTasksForAnimation(apps, false /*splitExpectedToBeVisible*/); + } finally { + for (RemoteAnimationTarget appTarget : apps) { + if (appTarget.leash != null) { + appTarget.leash.release(); + } + } + } } private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps, @@ -603,13 +666,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } @Override - public void onKeyguardVisibilityChanged(boolean showing) { - mMainExecutor.execute(() -> { - SplitScreenController.this.onKeyguardVisibilityChanged(showing); - }); - } - - @Override public void onFinishedWakingUp() { mMainExecutor.execute(() -> { SplitScreenController.this.onFinishedWakingUp(); 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 e55729a883e0..056cd5813861 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 @@ -246,7 +246,9 @@ class SplitScreenTransitions { return true; } - void onTransitionMerged(@NonNull IBinder transition) { + void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) { + if (aborted) return; + // Once a pending enter transition got merged, make sure to append the reset of finishing // operations to the finish transition. if (transition == mPendingEnter) { 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 59b0afe22acb..2229e26b9343 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 @@ -35,6 +35,8 @@ 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.CONTROLLED_ACTIVITY_TYPES; +import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES; 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; @@ -87,6 +89,7 @@ import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.WindowManager; +import android.widget.Toast; import android.window.DisplayAreaInfo; import android.window.RemoteTransition; import android.window.TransitionInfo; @@ -97,7 +100,9 @@ import android.window.WindowContainerTransaction; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.InstanceId; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.ArrayUtils; import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; @@ -136,7 +141,7 @@ import java.util.Optional; */ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, DisplayController.OnDisplaysChangedListener, Transitions.TransitionHandler, - ShellTaskOrganizer.TaskListener { + ShellTaskOrganizer.TaskListener, ShellTaskOrganizer.FocusListener { private static final String TAG = StageCoordinator.class.getSimpleName(); @@ -171,6 +176,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private final ShellExecutor mMainExecutor; private final Optional<RecentTasksController> mRecentTasks; + private final Rect mTempRect1 = new Rect(); + private final Rect mTempRect2 = new Rect(); + + private ActivityManager.RunningTaskInfo mFocusingTaskInfo; + /** * A single-top root task which the split divider attached to. */ @@ -184,6 +194,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private boolean mShouldUpdateRecents; private boolean mExitSplitScreenOnHide; private boolean mIsDividerRemoteAnimating; + private boolean mIsExiting; private boolean mResizingSplits; /** The target stage to dismiss to when unlock after folded. */ @@ -249,6 +260,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDisplayController.addDisplayWindowListener(this); mDisplayLayout = new DisplayLayout(displayController.getDisplayLayout(displayId)); transitions.addHandler(this); + mTaskOrganizer.addFocusListener(this); } @VisibleForTesting @@ -437,7 +449,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, new IRemoteAnimationFinishedCallback.Stub() { @Override public void onAnimationFinished() throws RemoteException { - onRemoteAnimationFinishedOrCancelled(evictWct); + onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct); finishedCallback.onAnimationFinished(); } }; @@ -458,7 +470,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onAnimationCancelled(boolean isKeyguardOccluded) { - onRemoteAnimationFinishedOrCancelled(evictWct); + onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct); try { adapter.getRunner().onAnimationCancelled(isKeyguardOccluded); } catch (RemoteException e) { @@ -508,13 +520,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, }); } - private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) { + private void onRemoteAnimationFinishedOrCancelled(boolean cancel, + WindowContainerTransaction evictWct) { mIsDividerRemoteAnimating = false; mShouldUpdateRecents = true; // If any stage has no child after animation finished, it means that split will display // nothing, such status will happen if task and intent is same app but not support // multi-instagce, we should exit split and expand that app as full screen. - if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) { + if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) { mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0 ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); @@ -728,7 +741,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void applyExitSplitScreen(@Nullable StageTaskListener childrenToTop, WindowContainerTransaction wct, @ExitReason int exitReason) { - if (!mMainStage.isActive()) return; + if (!mMainStage.isActive() || mIsExiting) return; mRecentTasks.ifPresent(recentTasks -> { // Notify recents if we are exiting in a way that breaks the pair, and disable further @@ -740,22 +753,45 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, }); mShouldUpdateRecents = false; - // When the exit split-screen is caused by one of the task enters auto pip, - // we want the tasks to be put to bottom instead of top, otherwise it will end up - // a fullscreen plus a pinned task instead of pinned only at the end of the transition. - final boolean fromEnteringPip = exitReason == EXIT_REASON_CHILD_TASK_ENTER_PIP; - mSideStage.removeAllTasks(wct, !fromEnteringPip && mSideStage == childrenToTop); - mMainStage.deactivate(wct, !fromEnteringPip && mMainStage == childrenToTop); - wct.reorder(mRootTaskInfo.token, false /* onTop */); - mTaskOrganizer.applyTransaction(wct); + if (childrenToTop == null) { + mSideStage.removeAllTasks(wct, false /* toTop */); + mMainStage.deactivate(wct, false /* toTop */); + wct.reorder(mRootTaskInfo.token, false /* onTop */); + onTransitionAnimationComplete(); + } else { + // Expand to top side split as full screen for fading out decor animation and dismiss + // another side split(Moving its children to bottom). + mIsExiting = true; + final StageTaskListener tempFullStage = childrenToTop; + final StageTaskListener dismissStage = mMainStage == childrenToTop + ? mSideStage : mMainStage; + tempFullStage.resetBounds(wct); + wct.setSmallestScreenWidthDp(tempFullStage.mRootTaskInfo.token, + mRootTaskInfo.configuration.smallestScreenWidthDp); + dismissStage.dismiss(wct, false /* toTop */); + } + mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { - setResizingSplits(false /* resizing */); t.setWindowCrop(mMainStage.mRootLeash, null) .setWindowCrop(mSideStage.mRootLeash, null); + t.setPosition(mMainStage.mRootLeash, 0, 0) + .setPosition(mSideStage.mRootLeash, 0, 0); setDividerVisibility(false, t); + + // In this case, exit still under progress, fade out the split decor after first WCT + // done and do remaining WCT after animation finished. + if (childrenToTop != null) { + childrenToTop.fadeOutDecor(() -> { + WindowContainerTransaction finishedWCT = new WindowContainerTransaction(); + mIsExiting = false; + childrenToTop.dismiss(finishedWCT, true /* toTop */); + wct.reorder(mRootTaskInfo.token, false /* toTop */); + mTaskOrganizer.applyTransaction(finishedWCT); + onTransitionAnimationComplete(); + }); + } }); - onTransitionAnimationComplete(); Slog.i(TAG, "applyExitSplitScreen, reason = " + exitReasonToString(exitReason)); // Log the exit if (childrenToTop != null) { @@ -925,9 +961,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - private void onStageChildTaskEnterPip(StageListenerImpl stageListener, int taskId) { - exitSplitScreen(stageListener == mMainStageListener ? mMainStage : mSideStage, - EXIT_REASON_CHILD_TASK_ENTER_PIP); + private void onStageChildTaskEnterPip() { + // When the exit split-screen is caused by one of the task enters auto pip, + // we want both tasks to be put to bottom instead of top, otherwise it will end up + // a fullscreen plus a pinned task instead of pinned only at the end of the transition. + exitSplitScreen(null, EXIT_REASON_CHILD_TASK_ENTER_PIP); } private void updateRecentTasksSplitPair() { @@ -1022,6 +1060,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } mRootTaskInfo = null; + mRootTaskLeash = null; } @@ -1148,12 +1187,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mDividerFadeInAnimator.cancel(); return; } + mSplitLayout.getRefDividerBounds(mTempRect1); transaction.show(dividerLeash); transaction.setAlpha(dividerLeash, 0); transaction.setLayer(dividerLeash, Integer.MAX_VALUE); - transaction.setPosition(dividerLeash, - mSplitLayout.getRefDividerBounds().left, - mSplitLayout.getRefDividerBounds().top); + transaction.setPosition(dividerLeash, mTempRect1.left, mTempRect1.top); transaction.apply(); } @@ -1173,21 +1211,42 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, private void onStageHasChildrenChanged(StageListenerImpl stageListener) { final boolean hasChildren = stageListener.mHasChildren; final boolean isSideStage = stageListener == mSideStageListener; - if (!hasChildren) { + if (!hasChildren && !mIsExiting) { if (isSideStage && mMainStageListener.mVisible) { // Exit to main stage if side stage no longer has children. - exitSplitScreen(mMainStage, EXIT_REASON_APP_FINISHED); + if (ENABLE_SHELL_TRANSITIONS) { + exitSplitScreen(mMainStage, EXIT_REASON_APP_FINISHED); + } else { + mSplitLayout.flingDividerToDismiss( + mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT, + EXIT_REASON_APP_FINISHED); + } } else if (!isSideStage && mSideStageListener.mVisible) { // Exit to side stage if main stage no longer has children. - exitSplitScreen(mSideStage, EXIT_REASON_APP_FINISHED); + if (ENABLE_SHELL_TRANSITIONS) { + exitSplitScreen(mSideStage, EXIT_REASON_APP_FINISHED); + } else { + mSplitLayout.flingDividerToDismiss( + mSideStagePosition != SPLIT_POSITION_BOTTOM_OR_RIGHT, + EXIT_REASON_APP_FINISHED); + } } } else if (isSideStage && !mMainStage.isActive()) { - final WindowContainerTransaction wct = new WindowContainerTransaction(); - mSplitLayout.init(); - prepareEnterSplitScreen(wct); - mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> - updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */)); + if (mFocusingTaskInfo != null && !isValidToEnterSplitScreen(mFocusingTaskInfo)) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + mSideStage.removeAllTasks(wct, true); + wct.reorder(mRootTaskInfo.token, false /* onTop */); + mTaskOrganizer.applyTransaction(wct); + Slog.i(TAG, "cancel entering split screen, reason = " + + exitReasonToString(EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW)); + } else { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + mSplitLayout.init(); + prepareEnterSplitScreen(wct); + mSyncQueue.queue(wct); + mSyncQueue.runInSync(t -> + updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */)); + } } if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) { mShouldUpdateRecents = true; @@ -1202,17 +1261,31 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } + boolean isValidToEnterSplitScreen(@NonNull ActivityManager.RunningTaskInfo taskInfo) { + return taskInfo.supportsMultiWindow + && ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType()) + && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode()); + } + + ActivityManager.RunningTaskInfo getFocusingTaskInfo() { + return mFocusingTaskInfo; + } + @Override - public void onSnappedToDismiss(boolean bottomOrRight) { + public void onFocusTaskChanged(ActivityManager.RunningTaskInfo taskInfo) { + mFocusingTaskInfo = taskInfo; + } + + @Override + public void onSnappedToDismiss(boolean bottomOrRight, int reason) { final boolean mainStageToTop = bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT : mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT; if (!ENABLE_SHELL_TRANSITIONS) { - exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER); + exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, reason); return; } - setResizingSplits(false /* resizing */); final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE; final WindowContainerTransaction wct = new WindowContainerTransaction(); prepareExitSplitScreen(dismissTop, wct); @@ -1243,10 +1316,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, public void onLayoutSizeChanging(SplitLayout layout) { final SurfaceControl.Transaction t = mTransactionPool.acquire(); t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); - setResizingSplits(true /* resizing */); updateSurfaceBounds(layout, t, true /* applyResizingOffset */); - mMainStage.onResizing(getMainStageBounds(), t); - mSideStage.onResizing(getSideStageBounds(), t); + getMainStageBounds(mTempRect1); + getSideStageBounds(mTempRect2); + mMainStage.onResizing(mTempRect1, mTempRect2, t); + mSideStage.onResizing(mTempRect2, mTempRect1, t); t.apply(); mTransactionPool.release(t); } @@ -1258,7 +1332,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, sendOnBoundsChanged(); mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { - setResizingSplits(false /* resizing */); updateSurfaceBounds(layout, t, false /* applyResizingOffset */); mMainStage.onResized(t); mSideStage.onResized(t); @@ -1293,16 +1366,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, applyResizingOffset); } - void setResizingSplits(boolean resizing) { - if (resizing == mResizingSplits) return; - try { - ActivityTaskManager.getService().setSplitScreenResizing(resizing); - mResizingSplits = resizing; - } catch (RemoteException e) { - Slog.w(TAG, "Error calling setSplitScreenResizing", e); - } - } - @Override public int getSplitItemPosition(WindowContainerToken token) { if (token == null) { @@ -1353,8 +1416,6 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, 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()); @@ -1386,6 +1447,22 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, ? mSplitLayout.getBounds2() : mSplitLayout.getBounds1(); } + private void getSideStageBounds(Rect rect) { + if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) { + mSplitLayout.getBounds1(rect); + } else { + mSplitLayout.getBounds2(rect); + } + } + + private void getMainStageBounds(Rect rect) { + if (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT) { + mSplitLayout.getBounds2(rect); + } else { + mSplitLayout.getBounds1(rect); + } + } + /** * 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. @@ -1527,8 +1604,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } @Override - public void onTransitionMerged(@NonNull IBinder transition) { - mSplitTransitions.onTransitionMerged(transition); + public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) { + mSplitTransitions.onTransitionConsumed(transition, aborted); } @Override @@ -1607,7 +1684,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** 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()) { + if (!mMainStage.isActive() && !mIsExiting) { // Update divider state after animation so that it is still around and positioned // properly for the animation itself. mSplitLayout.release(); @@ -1903,8 +1980,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } @Override - public void onChildTaskEnterPip(int taskId) { - StageCoordinator.this.onStageChildTaskEnterPip(this, taskId); + public void onChildTaskEnterPip() { + StageCoordinator.this.onStageChildTaskEnterPip(); } @Override @@ -1916,10 +1993,13 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onNoLongerSupportMultiWindow() { if (mMainStage.isActive()) { + final Toast splitUnsupportedToast = Toast.makeText(mContext, + R.string.dock_non_resizeble_failed_to_dock_text, Toast.LENGTH_SHORT); final boolean isMainStage = mMainStageListener == this; if (!ENABLE_SHELL_TRANSITIONS) { StageCoordinator.this.exitSplitScreen(isMainStage ? mMainStage : mSideStage, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); + splitUnsupportedToast.show(); return; } @@ -1928,6 +2008,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, prepareExitSplitScreen(stageType, wct); mSplitTransitions.startDismissTransition(wct,StageCoordinator.this, stageType, EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW); + splitUnsupportedToast.show(); } } 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 23eec96a5d8f..f6dc68be946a 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 @@ -17,12 +17,12 @@ package com.android.wm.shell.splitscreen; import static android.app.ActivityTaskManager.INVALID_TASK_ID; -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_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES; +import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import android.annotation.CallSuper; @@ -40,6 +40,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; +import com.android.internal.util.ArrayUtils; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.SurfaceUtils; @@ -62,12 +63,6 @@ import java.util.function.Predicate; 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(); @@ -76,7 +71,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { void onChildTaskStatusChanged(int taskId, boolean present, boolean visible); - void onChildTaskEnterPip(int taskId); + void onChildTaskEnterPip(); void onRootTaskVanished(); @@ -108,6 +103,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this); } + /** + * General function for dismiss this stage. + */ + void dismiss(WindowContainerTransaction wct, boolean toTop) {} + int getChildCount() { return mChildrenTaskInfo.size(); } @@ -200,11 +200,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { @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) { // Inflates split decor view only when the root task is visible. if (mRootTaskInfo.isVisible != taskInfo.isVisible) { @@ -217,6 +212,15 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } mRootTaskInfo = taskInfo; } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) { + if (!taskInfo.supportsMultiWindow + || !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType()) + || !ArrayUtils.contains(CONTROLLED_WINDOWING_MODES_WHEN_ACTIVE, + taskInfo.getWindowingMode())) { + // Leave split screen if the task no longer supports multi window or have + // uncontrolled task. + mCallbacks.onNoLongerSupportMultiWindow(); + return; + } mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */, taskInfo.isVisible); @@ -242,6 +246,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { if (mRootTaskInfo.taskId == taskId) { mCallbacks.onRootTaskVanished(); mRootTaskInfo = null; + mRootLeash = null; mSyncQueue.runInSync(t -> { t.remove(mDimLayer); mSplitDecorManager.release(t); @@ -255,7 +260,7 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { return; } if (taskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) { - mCallbacks.onChildTaskEnterPip(taskId); + mCallbacks.onChildTaskEnterPip(); } sendStatusChanged(); } else { @@ -285,9 +290,9 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } } - void onResizing(Rect newBounds, SurfaceControl.Transaction t) { + void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t) { if (mSplitDecorManager != null && mRootTaskInfo != null) { - mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, t); + mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t); } } @@ -297,6 +302,14 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } } + void fadeOutDecor(Runnable finishedCallback) { + if (mSplitDecorManager != null) { + mSplitDecorManager.fadeOutDecor(finishedCallback); + } else { + finishedCallback.run(); + } + } + void addTask(ActivityManager.RunningTaskInfo task, WindowContainerTransaction wct) { // Clear overridden bounds and windowing mode to make sure the child task can inherit // windowing mode and bounds from split root. @@ -330,6 +343,11 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } } + void resetBounds(WindowContainerTransaction wct) { + wct.setBounds(mRootTaskInfo.token, null); + wct.setAppBounds(mRootTaskInfo.token, null); + } + void onSplitScreenListenerRegistered(SplitScreen.SplitScreenListener listener, @StageType int stage) { for (int i = mChildrenTaskInfo.size() - 1; i >= 0; --i) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ConfigurationChangeListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ConfigurationChangeListener.java new file mode 100644 index 000000000000..2fca8f0ecc76 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ConfigurationChangeListener.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.sysui; + +import android.content.res.Configuration; + +/** + * Callbacks for when the configuration changes. + */ +public interface ConfigurationChangeListener { + + /** + * Called when a configuration changes. This precedes all the following callbacks. + */ + default void onConfigurationChanged(Configuration newConfiguration) {} + + /** + * Convenience method to the above, called when the density or font scale changes. + */ + default void onDensityOrFontScaleChanged() {} + + /** + * Convenience method to the above, called when the smallest screen width changes. + */ + default void onSmallestScreenWidthChanged() {} + + /** + * Convenience method to the above, called when the system theme changes, including dark/light + * UI_MODE changes. + */ + default void onThemeChanged() {} + + /** + * Convenience method to the above, called when the local list or layout direction changes. + */ + default void onLocaleOrLayoutDirectionChanged() {} +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java new file mode 100644 index 000000000000..1c0b35894acd --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/KeyguardChangeListener.java @@ -0,0 +1,36 @@ +/* + * 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.sysui; + +/** + * Callbacks for when the keyguard changes. + */ +public interface KeyguardChangeListener { + /** + * Notifies the Shell that the keyguard is showing (and if so, whether it is occluded). + */ + default void onKeyguardVisibilityChanged(boolean visible, boolean occluded, + boolean animatingDismiss) {} + + /** + * Notifies the Shell when the keyguard dismiss animation has finished. + * + * TODO(b/206741900) deprecate this path once we're able to animate the PiP window as part of + * keyguard dismiss animation. + */ + default void onKeyguardDismissAnimationFinished() {} +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java new file mode 100644 index 000000000000..837acecef56f --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java @@ -0,0 +1,187 @@ +/* + * 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.sysui; + +import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS; +import static android.content.pm.ActivityInfo.CONFIG_FONT_SCALE; +import static android.content.pm.ActivityInfo.CONFIG_LAYOUT_DIRECTION; +import static android.content.pm.ActivityInfo.CONFIG_LOCALE; +import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE; +import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; + +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SYSUI_EVENTS; + +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; + +import com.android.internal.protolog.common.ProtoLog; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.annotations.ExternalThread; + +import java.io.PrintWriter; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Handles event callbacks from SysUI that can be used within the Shell. + */ +public class ShellController { + private static final String TAG = ShellController.class.getSimpleName(); + + private final ShellExecutor mMainExecutor; + private final ShellInterfaceImpl mImpl = new ShellInterfaceImpl(); + + private final CopyOnWriteArrayList<ConfigurationChangeListener> mConfigChangeListeners = + new CopyOnWriteArrayList<>(); + private final CopyOnWriteArrayList<KeyguardChangeListener> mKeyguardChangeListeners = + new CopyOnWriteArrayList<>(); + private Configuration mLastConfiguration; + + + public ShellController(ShellExecutor mainExecutor) { + mMainExecutor = mainExecutor; + } + + /** + * Returns the external interface to this controller. + */ + public ShellInterface asShell() { + return mImpl; + } + + /** + * Adds a new configuration listener. The configuration change callbacks are not made in any + * particular order. + */ + public void addConfigurationChangeListener(ConfigurationChangeListener listener) { + mConfigChangeListeners.remove(listener); + mConfigChangeListeners.add(listener); + } + + /** + * Removes an existing configuration listener. + */ + public void removeConfigurationChangeListener(ConfigurationChangeListener listener) { + mConfigChangeListeners.remove(listener); + } + + /** + * Adds a new Keyguard listener. The Keyguard change callbacks are not made in any + * particular order. + */ + public void addKeyguardChangeListener(KeyguardChangeListener listener) { + mKeyguardChangeListeners.remove(listener); + mKeyguardChangeListeners.add(listener); + } + + /** + * Removes an existing Keyguard listener. + */ + public void removeKeyguardChangeListener(KeyguardChangeListener listener) { + mKeyguardChangeListeners.remove(listener); + } + + @VisibleForTesting + void onConfigurationChanged(Configuration newConfig) { + // The initial config is send on startup and doesn't trigger listener callbacks + if (mLastConfiguration == null) { + mLastConfiguration = new Configuration(newConfig); + ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "Initial Configuration: %s", newConfig); + return; + } + + final int diff = newConfig.diff(mLastConfiguration); + ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "New configuration change: %s", newConfig); + ProtoLog.v(WM_SHELL_SYSUI_EVENTS, "\tchanges=%s", + Configuration.configurationDiffToString(diff)); + final boolean densityFontScaleChanged = (diff & CONFIG_FONT_SCALE) != 0 + || (diff & ActivityInfo.CONFIG_DENSITY) != 0; + final boolean smallestScreenWidthChanged = (diff & CONFIG_SMALLEST_SCREEN_SIZE) != 0; + final boolean themeChanged = (diff & CONFIG_ASSETS_PATHS) != 0 + || (diff & CONFIG_UI_MODE) != 0; + final boolean localOrLayoutDirectionChanged = (diff & CONFIG_LOCALE) != 0 + || (diff & CONFIG_LAYOUT_DIRECTION) != 0; + + // Update the last configuration and call listeners + mLastConfiguration.updateFrom(newConfig); + for (ConfigurationChangeListener listener : mConfigChangeListeners) { + listener.onConfigurationChanged(newConfig); + if (densityFontScaleChanged) { + listener.onDensityOrFontScaleChanged(); + } + if (smallestScreenWidthChanged) { + listener.onSmallestScreenWidthChanged(); + } + if (themeChanged) { + listener.onThemeChanged(); + } + if (localOrLayoutDirectionChanged) { + listener.onLocaleOrLayoutDirectionChanged(); + } + } + } + + @VisibleForTesting + void onKeyguardVisibilityChanged(boolean visible, boolean occluded, boolean animatingDismiss) { + for (KeyguardChangeListener listener : mKeyguardChangeListeners) { + listener.onKeyguardVisibilityChanged(visible, occluded, animatingDismiss); + } + } + + @VisibleForTesting + void onKeyguardDismissAnimationFinished() { + for (KeyguardChangeListener listener : mKeyguardChangeListeners) { + listener.onKeyguardDismissAnimationFinished(); + } + } + + public void dump(@NonNull PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + TAG); + pw.println(innerPrefix + "mConfigChangeListeners=" + mConfigChangeListeners.size()); + pw.println(innerPrefix + "mLastConfiguration=" + mLastConfiguration); + pw.println(innerPrefix + "mKeyguardChangeListeners=" + mKeyguardChangeListeners.size()); + } + + /** + * The interface for calls from outside the Shell, within the host process. + */ + @ExternalThread + private class ShellInterfaceImpl implements ShellInterface { + @Override + public void onConfigurationChanged(Configuration newConfiguration) { + mMainExecutor.execute(() -> + ShellController.this.onConfigurationChanged(newConfiguration)); + } + + @Override + public void onKeyguardVisibilityChanged(boolean visible, boolean occluded, + boolean animatingDismiss) { + mMainExecutor.execute(() -> + ShellController.this.onKeyguardVisibilityChanged(visible, occluded, + animatingDismiss)); + } + + @Override + public void onKeyguardDismissAnimationFinished() { + mMainExecutor.execute(() -> + ShellController.this.onKeyguardDismissAnimationFinished()); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java new file mode 100644 index 000000000000..a15ce5d2b816 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellInterface.java @@ -0,0 +1,45 @@ +/* + * 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.sysui; + +import android.content.res.Configuration; + +/** + * General interface for notifying the Shell of common SysUI events like configuration or keyguard + * changes. + * + * TODO: Move ShellInit and ShellCommandHandler into this interface + */ +public interface ShellInterface { + + /** + * Notifies the Shell that the configuration has changed. + */ + default void onConfigurationChanged(Configuration newConfiguration) {} + + /** + * Notifies the Shell that the keyguard is showing (and if so, whether it is occluded) or not + * showing, and whether it is animating a dismiss. + */ + default void onKeyguardVisibilityChanged(boolean visible, boolean occluded, + boolean animatingDismiss) {} + + /** + * Notifies the Shell when the keyguard dismiss animation has finished. + */ + default void onKeyguardDismissAnimationFinished() {} +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java deleted file mode 100644 index ad9dda619370..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelper.java +++ /dev/null @@ -1,37 +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.tasksurfacehelper; - -import android.app.ActivityManager.RunningTaskInfo; -import android.graphics.Rect; -import android.view.SurfaceControl; - -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -/** - * Interface to communicate with a Task's SurfaceControl. - */ -public interface TaskSurfaceHelper { - - /** Sets the METADATA_GAME_MODE for the layer corresponding to the task **/ - default void setGameModeForTask(int taskId, int gameMode) {} - - /** Takes a screenshot for a task **/ - default void screenshotTask(RunningTaskInfo taskInfo, Rect crop, Executor executor, - Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {} -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.java deleted file mode 100644 index 064d9d1231c1..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperController.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.tasksurfacehelper; - -import android.app.ActivityManager.RunningTaskInfo; -import android.graphics.Rect; -import android.view.SurfaceControl; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.ShellExecutor; - -import java.util.concurrent.Executor; -import java.util.function.Consumer; - -/** - * Intermediary controller that communicates with {@link ShellTaskOrganizer} to send commands - * to SurfaceControl. - */ -public class TaskSurfaceHelperController { - - private final ShellTaskOrganizer mTaskOrganizer; - private final ShellExecutor mMainExecutor; - private final TaskSurfaceHelperImpl mImpl = new TaskSurfaceHelperImpl(); - - public TaskSurfaceHelperController(ShellTaskOrganizer taskOrganizer, - ShellExecutor mainExecutor) { - mTaskOrganizer = taskOrganizer; - mMainExecutor = mainExecutor; - } - - public TaskSurfaceHelper asTaskSurfaceHelper() { - return mImpl; - } - - /** - * Sends a Transaction to set the game mode metadata on the - * corresponding SurfaceControl - */ - public void setGameModeForTask(int taskId, int gameMode) { - mTaskOrganizer.setSurfaceMetadata(taskId, SurfaceControl.METADATA_GAME_MODE, gameMode); - } - - /** - * Take screenshot of the specified task. - */ - public void screenshotTask(RunningTaskInfo taskInfo, Rect crop, - Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) { - mTaskOrganizer.screenshotTask(taskInfo, crop, consumer); - } - - private class TaskSurfaceHelperImpl implements TaskSurfaceHelper { - @Override - public void setGameModeForTask(int taskId, int gameMode) { - mMainExecutor.execute(() -> { - TaskSurfaceHelperController.this.setGameModeForTask(taskId, gameMode); - }); - } - - @Override - public void screenshotTask(RunningTaskInfo taskInfo, Rect crop, Executor executor, - Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) { - mMainExecutor.execute(() -> { - TaskSurfaceHelperController.this.screenshotTask(taskInfo, crop, - (t) -> executor.execute(() -> consumer.accept(t))); - }); - } - } -} 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 index 7234d559e153..11b453cb24a2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java @@ -274,7 +274,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler { } @Override - public void onTransitionMerged(@NonNull IBinder transition) { + public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) { MixedTransition mixed = null; for (int i = mActiveTransitions.size() - 1; i >= 0; --i) { if (mActiveTransitions.get(i).mTransition != transition) continue; @@ -283,7 +283,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler { } if (mixed == null) return; if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { - mPipHandler.onTransitionMerged(transition); + mPipHandler.onTransitionConsumed(transition, aborted); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java index 3e2a0e635a75..ebaece2189aa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java @@ -99,6 +99,8 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { + " during unit tests"); } mRemote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb); + // assume that remote will apply the start transaction. + startTransaction.clear(); } catch (RemoteException e) { Log.e(Transitions.TAG, "Error running remote transition.", e); if (mRemote.asBinder() != null) { @@ -120,6 +122,11 @@ public class OneShotRemoteHandler implements Transitions.TransitionHandler { @Override public void onTransitionFinished(WindowContainerTransaction wct, SurfaceControl.Transaction sct) { + // We have merged, since we sent the transaction over binder, the one in this + // process won't be cleared if the remote applied it. We don't actually know if the + // remote applied the transaction, but applying twice will break surfaceflinger + // so just assume the worst-case and clear the local transaction. + t.clear(); mMainExecutor.execute( () -> finishCallback.onTransitionFinished(wct, null /* wctCB */)); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java index ece9f47e8788..b15c48cb5889 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java @@ -83,7 +83,7 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { } @Override - public void onTransitionMerged(@NonNull IBinder transition) { + public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) { mRequestedRemotes.remove(transition); } @@ -139,6 +139,8 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { + " during unit tests"); } remote.getRemoteTransition().startAnimation(transition, info, startTransaction, cb); + // assume that remote will apply the start transaction. + startTransaction.clear(); } catch (RemoteException e) { Log.e(Transitions.TAG, "Error running remote transition.", e); unhandleDeath(remote.asBinder(), finishCallback); @@ -162,6 +164,11 @@ public class RemoteTransitionHandler implements Transitions.TransitionHandler { @Override public void onTransitionFinished(WindowContainerTransaction wct, SurfaceControl.Transaction sct) { + // We have merged, since we sent the transaction over binder, the one in this + // process won't be cleared if the remote applied it. We don't actually know if the + // remote applied the transaction, but applying twice will break surfaceflinger + // so just assume the worst-case and clear the local transaction. + t.clear(); mMainExecutor.execute(() -> { if (!mRequestedRemotes.containsKey(mergeTarget)) { Log.e(TAG, "Merged transition finished after it's mergeTarget (the " 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 de0f47fa0a6b..fa22c7ca94d2 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 @@ -516,15 +516,20 @@ public class Transitions implements RemoteCallable<Transitions> { active.mMerged = true; active.mAborted = abort; if (active.mHandler != null) { - active.mHandler.onTransitionMerged(active.mToken); + active.mHandler.onTransitionConsumed(active.mToken, abort); } return; } - mActiveTransitions.get(activeIdx).mAborted = abort; + final ActiveTransition active = mActiveTransitions.get(activeIdx); + active.mAborted = abort; + if (active.mAborted && active.mHandler != null) { + // Notifies to clean-up the aborted transition. + active.mHandler.onTransitionConsumed(transition, true /* aborted */); + } ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished (abort=%b), notifying core %s", abort, transition); // Merge all relevant transactions together - SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT; + SurfaceControl.Transaction fullFinish = active.mFinishT; for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) { final ActiveTransition toMerge = mActiveTransitions.get(iA); if (!toMerge.mMerged) break; @@ -553,6 +558,10 @@ public class Transitions implements RemoteCallable<Transitions> { while (mActiveTransitions.size() > activeIdx && mActiveTransitions.get(activeIdx).mAborted) { ActiveTransition aborted = mActiveTransitions.remove(activeIdx); + // Notifies to clean-up the aborted transition. + if (aborted.mHandler != null) { + aborted.mHandler.onTransitionConsumed(transition, true /* aborted */); + } mOrganizer.finishTransition(aborted.mToken, null /* wct */, null /* wctCB */); } if (mActiveTransitions.size() <= activeIdx) { @@ -735,9 +744,10 @@ public class Transitions implements RemoteCallable<Transitions> { /** * Called when a transition which was already "claimed" by this handler has been merged - * into another animation. Gives this handler a chance to clean-up any expectations. + * into another animation or has been aborted. Gives this handler a chance to clean-up any + * expectations. */ - default void onTransitionMerged(@NonNull IBinder transition) { } + default void onTransitionConsumed(@NonNull IBinder transition, boolean aborted) { } /** * Sets transition animation scale settings value to handler. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java index 572516d2f8c9..cdca051a4ee5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java @@ -95,6 +95,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL mDragResizeCallback = dragResizeCallback; } + @Override void relayout(ActivityManager.RunningTaskInfo taskInfo) { final int shadowRadiusDp = taskInfo.isFocused ? DECOR_SHADOW_FOCUSED_THICKNESS_IN_DIP : DECOR_SHADOW_UNFOCUSED_THICKNESS_IN_DIP; @@ -119,8 +120,8 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL }); if (mResult.mRootView == null) { - // This means the task is hidden. Nothing is set up in this case including the - // decoration surface. + // This means something blocks the window decor from showing, e.g. the task is hidden. + // Nothing is set up in this case including the decoration surface. return; } if (oldRootView != mResult.mRootView) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java index 0c5022832162..c19a33abf8a4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java @@ -38,6 +38,8 @@ import android.window.WindowContainerTransaction; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import java.util.function.Supplier; + /** * Manages a container surface and a windowless window to show window decoration. Responsible to * update window decoration window state and layout parameters on task info changes and so that @@ -53,7 +55,8 @@ import com.android.wm.shell.common.DisplayController; * * @param <T> The type of the root view */ -public class WindowDecoration<T extends View & TaskFocusStateConsumer> implements AutoCloseable { +public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> + implements AutoCloseable { private static final int[] CAPTION_INSETS_TYPES = { InsetsState.ITYPE_CAPTION_BAR }; /** @@ -62,6 +65,20 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement final Context mContext; final DisplayController mDisplayController; final ShellTaskOrganizer mTaskOrganizer; + final Supplier<SurfaceControl.Builder> mSurfaceControlBuilderSupplier; + final SurfaceControlViewHostFactory mSurfaceControlViewHostFactory; + private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener = + new DisplayController.OnDisplaysChangedListener() { + @Override + public void onDisplayAdded(int displayId) { + if (mTaskInfo.displayId != displayId) { + return; + } + + mDisplayController.removeDisplayWindowListener(this); + relayout(mTaskInfo); + } + }; RunningTaskInfo mTaskInfo; final SurfaceControl mTaskSurface; @@ -71,7 +88,7 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement SurfaceControl mDecorationContainerSurface; SurfaceControl mTaskBackgroundSurface; - private CaptionWindowManager mCaptionWindowManager; + private final CaptionWindowManager mCaptionWindowManager; private SurfaceControlViewHost mViewHost; private final Rect mCaptionInsetsRect = new Rect(); @@ -84,11 +101,25 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement ShellTaskOrganizer taskOrganizer, RunningTaskInfo taskInfo, SurfaceControl taskSurface) { + this(context, displayController, taskOrganizer, taskInfo, taskSurface, + SurfaceControl.Builder::new, new SurfaceControlViewHostFactory() {}); + } + + WindowDecoration( + Context context, + DisplayController displayController, + ShellTaskOrganizer taskOrganizer, + RunningTaskInfo taskInfo, + SurfaceControl taskSurface, + Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, + SurfaceControlViewHostFactory surfaceControlViewHostFactory) { mContext = context; mDisplayController = displayController; mTaskOrganizer = taskOrganizer; mTaskInfo = taskInfo; mTaskSurface = taskSurface; + mSurfaceControlBuilderSupplier = surfaceControlBuilderSupplier; + mSurfaceControlViewHostFactory = surfaceControlViewHostFactory; mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId); mDecorWindowContext = mContext.createConfigurationContext(mTaskInfo.getConfiguration()); @@ -99,6 +130,15 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement new CaptionWindowManager(mTaskInfo.getConfiguration(), mTaskSurface); } + /** + * Used by {@link WindowDecoration} to trigger a new relayout because the requirements for a + * relayout weren't satisfied are satisfied now. + * + * @param taskInfo The previous {@link RunningTaskInfo} passed into {@link #relayout} or the + * constructor. + */ + abstract void relayout(RunningTaskInfo taskInfo); + void relayout(RunningTaskInfo taskInfo, int layoutResId, T rootView, float captionHeightDp, Rect outsetsDp, float shadowRadiusDp, SurfaceControl.Transaction t, WindowContainerTransaction wct, RelayoutResult<T> outResult) { @@ -110,7 +150,7 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement } if (!mTaskInfo.isVisible) { - close(); + releaseViews(); t.hide(mTaskSurface); return; } @@ -123,10 +163,14 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement rootView = null; // Clear it just in case we use it accidentally final Configuration taskConfig = mTaskInfo.getConfiguration(); if (oldTaskConfig.densityDpi != taskConfig.densityDpi + || mDisplay == null || mDisplay.getDisplayId() != mTaskInfo.displayId) { - close(); + releaseViews(); - mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId); + if (!obtainDisplayOrRegisterListener()) { + outResult.mRootView = null; + return; + } mDecorWindowContext = mContext.createConfigurationContext(taskConfig); if (layoutResId != 0) { outResult.mRootView = @@ -141,7 +185,7 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement // DecorationContainerSurface if (mDecorationContainerSurface == null) { - final SurfaceControl.Builder builder = new SurfaceControl.Builder(); + final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); mDecorationContainerSurface = builder .setName("Decor container of Task=" + mTaskInfo.taskId) .setContainerLayer() @@ -168,7 +212,7 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement // TaskBackgroundSurface if (mTaskBackgroundSurface == null) { - final SurfaceControl.Builder builder = new SurfaceControl.Builder(); + final SurfaceControl.Builder builder = mSurfaceControlBuilderSupplier.get(); mTaskBackgroundSurface = builder .setName("Background of Task=" + mTaskInfo.taskId) .setEffectLayer() @@ -195,7 +239,7 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement lp.setTitle("Caption of Task=" + mTaskInfo.taskId); lp.setTrustedOverlay(); if (mViewHost == null) { - mViewHost = new SurfaceControlViewHost(mDecorWindowContext, mDisplay, + mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay, mCaptionWindowManager, true); mViewHost.setView(outResult.mRootView, lp); } else { @@ -225,8 +269,22 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement .show(mTaskSurface); } - @Override - public void close() { + /** + * Obtains the {@link Display} instance for the display ID in {@link #mTaskInfo} if it exists or + * registers {@link #mOnDisplaysChangedListener} if it doesn't. + * + * @return {@code true} if the {@link Display} instance exists; or {@code false} otherwise + */ + private boolean obtainDisplayOrRegisterListener() { + mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId); + if (mDisplay == null) { + mDisplayController.addDisplayWindowListener(mOnDisplaysChangedListener); + return false; + } + return true; + } + + private void releaseViews() { if (mViewHost != null) { mViewHost.release(); mViewHost = null; @@ -243,6 +301,12 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement } } + @Override + public void close() { + mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener); + releaseViews(); + } + static class RelayoutResult<T extends View & TaskFocusStateConsumer> { int mWidth; int mHeight; @@ -267,4 +331,11 @@ public class WindowDecoration<T extends View & TaskFocusStateConsumer> implement super.setConfiguration(configuration); } } + + interface SurfaceControlViewHostFactory { + default SurfaceControlViewHost create( + Context c, Display d, WindowlessWindowManager wmm, boolean useSfChoreographer) { + return new SurfaceControlViewHost(c, d, wmm, useSfChoreographer); + } + } } 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 f73d191b1917..8157a4e453af 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 @@ -111,6 +111,10 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( clickObject(ENTER_PIP_ON_USER_LEAVE_HINT) } + fun enableAutoEnterForPipActivity() { + clickObject(ENTER_PIP_AUTOENTER) + } + fun clickStartMediaSessionButton() { clickObject(MEDIA_SESSION_START_RADIO_BUTTON_ID) } @@ -203,5 +207,6 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper( 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" + private const val ENTER_PIP_AUTOENTER = "enter_pip_on_leave_autoenter" } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt new file mode 100644 index 000000000000..ce624f2b5bbe --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -0,0 +1,116 @@ +/* + * 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 android.platform.test.annotations.FlakyTest +import androidx.test.filters.RequiresDevice +import com.android.launcher3.tapl.LauncherInstrumentation +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.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test entering pip from an app via auto-enter property when navigating to home. + * + * To run this test: `atest WMShellFlickerTests:AutoEnterPipOnGoToHomeTest` + * + * Actions: + * Launch an app in full screen + * Select "Auto-enter PiP" radio button + * Press Home button or swipe up to go Home and 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) +@FlakyTest(bugId = 238367575) +@Group3 +class AutoEnterPipOnGoToHomeTest(testSpec: FlickerTestParameter) : EnterPipTest(testSpec) { + protected val taplInstrumentation = LauncherInstrumentation() + /** + * Defines the transition used to run the test + */ + override val transition: FlickerBuilder.() -> Unit + get() = { + setupAndTeardown(this) + setup { + eachRun { + pipApp.launchViaIntent(wmHelper) + pipApp.enableAutoEnterForPipActivity() + } + } + teardown { + eachRun { + // close gracefully so that onActivityUnpinned() can be called before force exit + pipApp.closePipWindow(wmHelper) + pipApp.exit(wmHelper) + } + } + transitions { + taplInstrumentation.goHome() + } + } + + override fun pipLayerReduces() { + val layerName = pipApp.component.toLayerName() + testSpec.assertLayers { + val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible } + pipLayerList.zipWithNext { previous, current -> + current.visibleRegion.notBiggerThan(previous.visibleRegion.region) + } + } + } + + /** + * Checks that [pipApp] window is animated towards default position in right bottom corner + */ + @Test + fun pipLayerMovesTowardsRightBottomCorner() { + // in gestural nav the swipe makes PiP first go upwards + Assume.assumeFalse(testSpec.isGesturalNavigation) + val layerName = pipApp.component.toLayerName() + testSpec.assertLayers { + val pipLayerList = this.layers { it.name.contains(layerName) && it.isVisible } + // Pip animates towards the right bottom corner, but because it is being resized at the + // same time, it is possible it shrinks first quickly below the default position and get + // moved up after that in just few last frames + pipLayerList.zipWithNext { previous, current -> + current.visibleRegion.isToTheRightBottom(previous.visibleRegion.region, 3) + } + } + } + + override fun focusChanges() { + // in gestural nav the focus goes to different activity on swipe up + Assume.assumeFalse(testSpec.isGesturalNavigation) + super.focusChanges() + } +} 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 index e7d641e9c66e..953f59a1f70b 100644 --- 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 @@ -36,7 +36,7 @@ import org.junit.runners.Parameterized * Actions: * Launch an app in full screen * Select "Via code behind" radio button - * Press Home button to put [pipApp] in pip mode + * Press Home button or swipe up to go Home and put [pipApp] in pip mode * * Notes: * 1. All assertions are inherited from [EnterPipTest] 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 e9e7bb61660d..229098313afa 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 @@ -64,6 +64,13 @@ android:layout_height="wrap_content" android:text="Via code behind" android:onClick="onAutoPipSelected"/> + + <RadioButton + android:id="@+id/enter_pip_on_leave_autoenter" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Auto-enter PiP" + android:onClick="onAutoPipSelected"/> </RadioGroup> <RadioGroup 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 a39aa4dc7bde..615b1730579c 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 @@ -236,6 +236,10 @@ public class PipActivity extends Activity { mPipParamsBuilder.setAutoEnterEnabled(false); setPictureInPictureParams(mPipParamsBuilder.build()); break; + case R.id.enter_pip_on_leave_autoenter: + mPipParamsBuilder.setAutoEnterEnabled(true); + setPictureInPictureParams(mPipParamsBuilder.build()); + break; } } diff --git a/libs/WindowManager/Shell/tests/unittest/Android.bp b/libs/WindowManager/Shell/tests/unittest/Android.bp index ea10be564351..1a8b9540cbd0 100644 --- a/libs/WindowManager/Shell/tests/unittest/Android.bp +++ b/libs/WindowManager/Shell/tests/unittest/Android.bp @@ -28,6 +28,9 @@ android_test { "**/*.java", "**/*.kt", ], + resource_dirs: [ + "res", + ], static_libs: [ "WindowManager-Shell", @@ -65,4 +68,9 @@ android_test { optimize: { enabled: false, }, + + aaptflags: [ + "--extra-packages", + "com.android.wm.shell.tests", + ], } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java new file mode 100644 index 000000000000..49228720b81d --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/MockSurfaceControlHelper.java @@ -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.wm.shell; + +import static org.mockito.Mockito.RETURNS_SELF; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; + +import android.view.SurfaceControl; + +/** + * Helper class to provide mocks for {@link SurfaceControl.Builder} and + * {@link SurfaceControl.Transaction} with method chaining support. + */ +public class MockSurfaceControlHelper { + private MockSurfaceControlHelper() {} + + /** + * Creates a mock {@link SurfaceControl.Builder} that supports method chaining and return the + * given {@link SurfaceControl} when calling {@link SurfaceControl.Builder#build()}. + * + * @param mockSurfaceControl the first {@link SurfaceControl} to return + * @param mockSurfaceControls following {@link SurfaceControl} to return + * @return the mock of {@link SurfaceControl.Builder} + */ + public static SurfaceControl.Builder createMockSurfaceControlBuilder( + SurfaceControl mockSurfaceControl, SurfaceControl... mockSurfaceControls) { + final SurfaceControl.Builder mockBuilder = mock(SurfaceControl.Builder.class, RETURNS_SELF); + doReturn(mockSurfaceControl, (Object[]) mockSurfaceControls) + .when(mockBuilder) + .build(); + return mockBuilder; + } + + /** + * Creates a mock {@link SurfaceControl.Transaction} that supports method chaining. + * @return the mock of {@link SurfaceControl.Transaction} + */ + public static SurfaceControl.Transaction createMockSurfaceControlTransaction() { + return mock(SurfaceControl.Transaction.class, RETURNS_SELF); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitImplTest.java new file mode 100644 index 000000000000..ace8d365c7af --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellInitImplTest.java @@ -0,0 +1,131 @@ +/* + * 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; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; + +import com.android.wm.shell.activityembedding.ActivityEmbeddingController; +import com.android.wm.shell.bubbles.BubbleController; +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.draganddrop.DragAndDropController; +import com.android.wm.shell.freeform.FreeformTaskListener; +import com.android.wm.shell.fullscreen.FullscreenTaskListener; +import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; +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.Transitions; +import com.android.wm.shell.unfold.UnfoldAnimationController; +import com.android.wm.shell.unfold.UnfoldTransitionHandler; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.Optional; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class ShellInitImplTest extends ShellTestCase { + + @Mock private DisplayController mDisplayController; + @Mock private DisplayImeController mDisplayImeController; + @Mock private DisplayInsetsController mDisplayInsetsController; + @Mock private DragAndDropController mDragAndDropController; + @Mock private ShellTaskOrganizer mShellTaskOrganizer; + @Mock private KidsModeTaskOrganizer mKidsModeTaskOrganizer; + @Mock private Optional<BubbleController> mBubblesOptional; + @Mock private Optional<SplitScreenController> mSplitScreenOptional; + @Mock private Optional<PipTouchHandler> mPipTouchHandlerOptional; + @Mock private FullscreenTaskListener mFullscreenTaskListener; + @Mock private Optional<UnfoldAnimationController> mUnfoldAnimationController; + @Mock private Optional<UnfoldTransitionHandler> mUnfoldTransitionHandler; + @Mock private Optional<FreeformTaskListener<?>> mFreeformTaskListenerOptional; + @Mock private Optional<RecentTasksController> mRecentTasks; + @Mock private Optional<ActivityEmbeddingController> mActivityEmbeddingController; + @Mock private Transitions mTransitions; + @Mock private StartingWindowController mStartingWindow; + @Mock private ShellExecutor mMainExecutor; + + private ShellInitImpl mImpl; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mImpl = new ShellInitImpl(mDisplayController, mDisplayImeController, + mDisplayInsetsController, mDragAndDropController, mShellTaskOrganizer, + mKidsModeTaskOrganizer, mBubblesOptional, mSplitScreenOptional, + mPipTouchHandlerOptional, mFullscreenTaskListener, mUnfoldAnimationController, + mUnfoldTransitionHandler, mFreeformTaskListenerOptional, mRecentTasks, + mActivityEmbeddingController, mTransitions, mStartingWindow, mMainExecutor); + } + + @Test + public void testAddInitCallbacks_expectCalledInOrder() { + ArrayList<Integer> results = new ArrayList<>(); + mImpl.addInitCallback(() -> { + results.add(1); + }, new Object()); + mImpl.addInitCallback(() -> { + results.add(2); + }, new Object()); + mImpl.addInitCallback(() -> { + results.add(3); + }, new Object()); + mImpl.init(); + assertTrue(results.get(0) == 1); + assertTrue(results.get(1) == 2); + assertTrue(results.get(2) == 3); + } + + @Test + public void testNoInitCallbacksAfterInit_expectException() { + mImpl.init(); + try { + mImpl.addInitCallback(() -> {}, new Object()); + fail("Expected exception when adding callback after init"); + } catch (IllegalArgumentException e) { + // Expected + } + } + + @Test + public void testDoubleInit_expectNoOp() { + ArrayList<Integer> results = new ArrayList<>(); + mImpl.addInitCallback(() -> { + results.add(1); + }, new Object()); + mImpl.init(); + assertTrue(results.size() == 1); + mImpl.init(); + assertTrue(results.size() == 1); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index 0b53c4069c3f..3dd00329253c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -26,11 +26,13 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP; +import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; @@ -46,6 +48,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.util.SparseArray; import android.view.SurfaceControl; +import android.view.SurfaceSession; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; import android.window.TaskAppearedInfo; @@ -76,7 +79,7 @@ import java.util.Optional; */ @SmallTest @RunWith(AndroidJUnit4.class) -public class ShellTaskOrganizerTests { +public class ShellTaskOrganizerTests extends ShellTestCase { @Mock private ITaskOrganizerController mTaskOrganizerController; @@ -137,13 +140,26 @@ public class ShellTaskOrganizerTests { } @Test - public void registerOrganizer_sendRegisterTaskOrganizer() throws RemoteException { + public void testRegisterOrganizer_sendRegisterTaskOrganizer() throws RemoteException { mOrganizer.registerOrganizer(); verify(mTaskOrganizerController).registerTaskOrganizer(any(ITaskOrganizer.class)); } @Test + public void testTaskLeashReleasedAfterVanished() throws RemoteException { + assumeFalse(ENABLE_SHELL_TRANSITIONS); + RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); + SurfaceControl taskLeash = new SurfaceControl.Builder(new SurfaceSession()) + .setName("task").build(); + mOrganizer.registerOrganizer(); + mOrganizer.onTaskAppeared(taskInfo, taskLeash); + assertTrue(taskLeash.isValid()); + mOrganizer.onTaskVanished(taskInfo); + assertTrue(!taskLeash.isValid()); + } + + @Test public void testOneListenerPerType() { mOrganizer.addListenerForType(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW); try { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java index 403dbf9d9554..b5ee037892ba 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java @@ -24,6 +24,8 @@ import android.testing.TestableContext; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.internal.protolog.common.ProtoLog; + import org.junit.After; import org.junit.Before; import org.mockito.MockitoAnnotations; @@ -37,6 +39,9 @@ public abstract class ShellTestCase { @Before public void shellSetup() { + // Disable protolog tool when running the tests from studio + ProtoLog.REQUIRE_PROTOLOGTOOL = false; + MockitoAnnotations.initMocks(this); final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java index 51eec27cfc0e..c0720cf04028 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java @@ -25,8 +25,10 @@ import static org.mockito.Mockito.mock; import android.app.ActivityManager; import android.app.WindowConfiguration; +import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; +import android.view.Display; import android.window.IWindowContainerToken; import android.window.WindowContainerToken; @@ -38,6 +40,10 @@ public final class TestRunningTaskInfoBuilder { private int mParentTaskId = INVALID_TASK_ID; private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD; private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED; + private int mDisplayId = Display.DEFAULT_DISPLAY; + private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null; + private final Point mPositionInParent = new Point(); + private boolean mIsVisible = false; public static WindowContainerToken createMockWCToken() { final IWindowContainerToken itoken = mock(IWindowContainerToken.class); @@ -68,17 +74,42 @@ public final class TestRunningTaskInfoBuilder { return this; } + public TestRunningTaskInfoBuilder setDisplayId(int displayId) { + mDisplayId = displayId; + return this; + } + + public TestRunningTaskInfoBuilder setTaskDescriptionBuilder( + ActivityManager.TaskDescription.Builder builder) { + mTaskDescriptionBuilder = builder; + return this; + } + + public TestRunningTaskInfoBuilder setPositionInParent(int x, int y) { + mPositionInParent.set(x, y); + return this; + } + + public TestRunningTaskInfoBuilder setVisible(boolean isVisible) { + mIsVisible = isVisible; + return this; + } + public ActivityManager.RunningTaskInfo build() { final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); - info.parentTaskId = INVALID_TASK_ID; info.taskId = sNextTaskId++; info.parentTaskId = mParentTaskId; + info.displayId = mDisplayId; info.configuration.windowConfiguration.setBounds(mBounds); info.configuration.windowConfiguration.setActivityType(mActivityType); info.configuration.windowConfiguration.setWindowingMode(mWindowingMode); info.token = mToken; info.isResizeable = true; info.supportsMultiWindow = true; + info.taskDescription = + mTaskDescriptionBuilder != null ? mTaskDescriptionBuilder.build() : null; + info.positionInParent = mPositionInParent; + info.isVisible = mIsVisible; return info; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java index e7c5cb2183db..31e55e7116f0 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java @@ -57,6 +57,7 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.util.test.FakeSettingsProvider; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import org.junit.Before; @@ -74,7 +75,7 @@ import org.mockito.MockitoAnnotations; @TestableLooper.RunWithLooper @SmallTest @RunWith(AndroidTestingRunner.class) -public class BackAnimationControllerTest { +public class BackAnimationControllerTest extends ShellTestCase { private static final String ANIMATION_ENABLED = "1"; private final TestShellExecutor mShellExecutor = new TestShellExecutor(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java index d5fbe556045a..0537d0ea2404 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTestActivity.java @@ -20,7 +20,7 @@ import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import com.android.wm.shell.R; +import com.android.wm.shell.tests.R; /** * Referenced by NotificationTestHelper#makeBubbleMetadata diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java index b88845044263..587782cb79ad 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java @@ -39,6 +39,7 @@ import android.view.SurfaceControl; import androidx.test.filters.SmallTest; import com.android.internal.view.IInputMethodManager; +import com.android.wm.shell.ShellTestCase; import org.junit.Before; import org.junit.Test; @@ -46,7 +47,7 @@ import org.junit.Test; import java.util.concurrent.Executor; @SmallTest -public class DisplayImeControllerTest { +public class DisplayImeControllerTest extends ShellTestCase { private SurfaceControl.Transaction mT; private DisplayImeController.PerDisplay mPerDisplay; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java index 3bf06cc0ede3..4a7fd3d259da 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayInsetsControllerTest.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import android.content.ComponentName; import android.os.RemoteException; import android.util.SparseArray; import android.view.IDisplayWindowInsetsController; @@ -34,6 +35,7 @@ import android.view.InsetsVisibilities; import androidx.test.filters.SmallTest; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import org.junit.Before; @@ -45,7 +47,7 @@ import org.mockito.MockitoAnnotations; import java.util.List; @SmallTest -public class DisplayInsetsControllerTest { +public class DisplayInsetsControllerTest extends ShellTestCase { private static final int SECOND_DISPLAY = DEFAULT_DISPLAY + 10; @@ -164,7 +166,7 @@ public class DisplayInsetsControllerTest { int hideInsetsCount = 0; @Override - public void topFocusedWindowChanged(String packageName, + public void topFocusedWindowChanged(ComponentName component, InsetsVisibilities requestedVisibilities) { topFocusedWindowChangedCount++; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java index 0ffa5b35331d..514390fa52f9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayLayoutTest.java @@ -41,6 +41,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.R; import com.android.internal.policy.SystemBarUtils; +import com.android.wm.shell.ShellTestCase; import org.junit.After; import org.junit.Before; @@ -54,7 +55,7 @@ import org.mockito.MockitoSession; * atest WMShellUnitTests:DisplayLayoutTest */ @SmallTest -public class DisplayLayoutTest { +public class DisplayLayoutTest extends ShellTestCase { private MockitoSession mMockitoSession; @Before diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java index 96938ebc27df..1347e061eb45 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/TaskStackListenerImplTest.java @@ -35,6 +35,8 @@ import android.window.TaskSnapshot; import androidx.test.filters.SmallTest; +import com.android.wm.shell.ShellTestCase; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -47,7 +49,7 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest -public class TaskStackListenerImplTest { +public class TaskStackListenerImplTest extends ShellTestCase { @Mock private IActivityTaskManager mActivityTaskManager; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java index f1e602fcf778..95725bbfd855 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitLayoutTests.java @@ -133,7 +133,7 @@ public class SplitLayoutTests extends ShellTestCase { mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget); waitDividerFlingFinished(); - verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false)); + verify(mSplitLayoutHandler).onSnappedToDismiss(eq(false), anyInt()); } @Test @@ -145,7 +145,7 @@ public class SplitLayoutTests extends ShellTestCase { mSplitLayout.snapToTarget(0 /* currentPosition */, snapTarget); waitDividerFlingFinished(); - verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true)); + verify(mSplitLayoutHandler).onSnappedToDismiss(eq(true), anyInt()); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index 596100dcdead..828c13ecfda6 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -53,6 +53,7 @@ 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.compatui.letterboxedu.LetterboxEduWindowManager; +import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.transition.Transitions; import org.junit.Before; @@ -78,6 +79,7 @@ public class CompatUIControllerTest extends ShellTestCase { private static final int TASK_ID = 12; private CompatUIController mController; + private @Mock ShellController mMockShellController; private @Mock DisplayController mMockDisplayController; private @Mock DisplayInsetsController mMockDisplayInsetsController; private @Mock DisplayLayout mMockDisplayLayout; @@ -105,7 +107,7 @@ public class CompatUIControllerTest extends ShellTestCase { doReturn(TASK_ID).when(mMockLetterboxEduLayout).getTaskId(); doReturn(true).when(mMockLetterboxEduLayout).createLayout(anyBoolean()); doReturn(true).when(mMockLetterboxEduLayout).updateCompatInfo(any(), any(), anyBoolean()); - mController = new CompatUIController(mContext, mMockDisplayController, + mController = new CompatUIController(mContext, mMockShellController, mMockDisplayController, mMockDisplayInsetsController, mMockImeController, mMockSyncQueue, mMockExecutor, mMockTransitionsLazy) { @Override @@ -124,6 +126,11 @@ public class CompatUIControllerTest extends ShellTestCase { } @Test + public void instantiateController_registerKeyguardChangeListener() { + verify(mMockShellController, times(1)).addKeyguardChangeListener(any()); + } + + @Test public void testListenerRegistered() { verify(mMockDisplayController).addDisplayWindowListener(mController); verify(mMockImeController).addPositionProcessor(mController); @@ -324,7 +331,7 @@ public class CompatUIControllerTest extends ShellTestCase { /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener); // Verify that the restart button is hidden after keyguard becomes showing. - mController.onKeyguardShowingChanged(true); + mController.onKeyguardVisibilityChanged(true, false, false); verify(mMockCompatLayout).updateVisibility(false); verify(mMockLetterboxEduLayout).updateVisibility(false); @@ -340,7 +347,7 @@ public class CompatUIControllerTest extends ShellTestCase { false); // Verify button is shown after keyguard becomes not showing. - mController.onKeyguardShowingChanged(false); + mController.onKeyguardVisibilityChanged(false, false, false); verify(mMockCompatLayout).updateVisibility(true); verify(mMockLetterboxEduLayout).updateVisibility(true); @@ -352,7 +359,7 @@ public class CompatUIControllerTest extends ShellTestCase { /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener); mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true); - mController.onKeyguardShowingChanged(true); + mController.onKeyguardVisibilityChanged(true, false, false); verify(mMockCompatLayout, times(2)).updateVisibility(false); verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false); @@ -360,7 +367,7 @@ public class CompatUIControllerTest extends ShellTestCase { clearInvocations(mMockCompatLayout, mMockLetterboxEduLayout); // Verify button remains hidden after keyguard becomes not showing since IME is showing. - mController.onKeyguardShowingChanged(false); + mController.onKeyguardVisibilityChanged(false, false, false); verify(mMockCompatLayout).updateVisibility(false); verify(mMockLetterboxEduLayout).updateVisibility(false); @@ -378,7 +385,7 @@ public class CompatUIControllerTest extends ShellTestCase { /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener); mController.onImeVisibilityChanged(DISPLAY_ID, /* isShowing= */ true); - mController.onKeyguardShowingChanged(true); + mController.onKeyguardVisibilityChanged(true, false, false); verify(mMockCompatLayout, times(2)).updateVisibility(false); verify(mMockLetterboxEduLayout, times(2)).updateVisibility(false); @@ -392,7 +399,7 @@ public class CompatUIControllerTest extends ShellTestCase { verify(mMockLetterboxEduLayout).updateVisibility(false); // Verify button is shown after keyguard becomes not showing. - mController.onKeyguardShowingChanged(false); + mController.onKeyguardVisibilityChanged(false, false, false); verify(mMockCompatLayout).updateVisibility(true); verify(mMockLetterboxEduLayout).updateVisibility(true); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java index aaeebef03d0f..e209971998c8 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java @@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.DragEvent.ACTION_DRAG_STARTED; import static org.junit.Assert.assertFalse; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -44,9 +45,11 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.UiEventLogger; import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.splitscreen.SplitScreenController; +import com.android.wm.shell.sysui.ShellController; import org.junit.Before; import org.junit.Test; @@ -61,28 +64,38 @@ import java.util.Optional; */ @SmallTest @RunWith(AndroidJUnit4.class) -public class DragAndDropControllerTest { +public class DragAndDropControllerTest extends ShellTestCase { @Mock private Context mContext; - + @Mock + private ShellController mShellController; @Mock private DisplayController mDisplayController; - @Mock private UiEventLogger mUiEventLogger; - @Mock private DragAndDropController.DragAndDropListener mDragAndDropListener; + @Mock + private IconProvider mIconProvider; + @Mock + private ShellExecutor mMainExecutor; + @Mock + private SplitScreenController mSplitScreenController; private DragAndDropController mController; @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); - mController = new DragAndDropController(mContext, mDisplayController, mUiEventLogger, - mock(IconProvider.class), mock(ShellExecutor.class)); - mController.initialize(Optional.of(mock(SplitScreenController.class))); + mController = new DragAndDropController(mContext, mShellController, mDisplayController, + mUiEventLogger, mIconProvider, mMainExecutor); + mController.initialize(Optional.of(mSplitScreenController)); + } + + @Test + public void instantiateController_registerConfigChangeListener() { + verify(mShellController, times(1)).addConfigurationChangeListener(any()); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java index 7e6595f1abe2..9e988e8e8726 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropPolicyTest.java @@ -34,7 +34,6 @@ import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPL import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_RIGHT; import static com.android.wm.shell.draganddrop.DragAndDropPolicy.Target.TYPE_SPLIT_TOP; -import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; @@ -57,7 +56,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; -import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Insets; @@ -68,6 +66,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.logging.InstanceId; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.draganddrop.DragAndDropPolicy.Target; import com.android.wm.shell.splitscreen.SplitScreenController; @@ -87,7 +86,7 @@ import java.util.HashSet; */ @SmallTest @RunWith(AndroidJUnit4.class) -public class DragAndDropPolicyTest { +public class DragAndDropPolicyTest extends ShellTestCase { @Mock private Context mContext; @@ -265,62 +264,6 @@ public class DragAndDropPolicyTest { } } - @Test - public void testLaunchMultipleTask_differentActivity() { - setRunningTask(mFullscreenAppTask); - mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); - Intent fillInIntent = mPolicy.getStartIntentFillInIntent(mock(PendingIntent.class), 0); - assertNull(fillInIntent); - } - - @Test - public void testLaunchMultipleTask_differentActivity_inSplitscreen() { - setRunningTask(mFullscreenAppTask); - doReturn(true).when(mSplitScreenStarter).isSplitScreenVisible(); - doReturn(mFullscreenAppTask).when(mSplitScreenStarter).getTaskInfo(anyInt()); - mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); - Intent fillInIntent = mPolicy.getStartIntentFillInIntent(mock(PendingIntent.class), 0); - assertNull(fillInIntent); - } - - @Test - public void testLaunchMultipleTask_sameActivity() { - setRunningTask(mFullscreenAppTask); - - // Replace the mocked drag pending intent and ensure it resolves to the same activity - PendingIntent launchIntent = mock(PendingIntent.class); - ResolveInfo launchInfo = new ResolveInfo(); - launchInfo.activityInfo = mFullscreenAppTask.topActivityInfo; - doReturn(Collections.singletonList(launchInfo)) - .when(launchIntent).queryIntentComponents(anyInt()); - mActivityClipData.getItemAt(0).getIntent().putExtra(ClipDescription.EXTRA_PENDING_INTENT, - launchIntent); - - mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); - Intent fillInIntent = mPolicy.getStartIntentFillInIntent(launchIntent, 0); - assertTrue((fillInIntent.getFlags() & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0); - } - - @Test - public void testLaunchMultipleTask_sameActivity_inSplitScreen() { - setRunningTask(mFullscreenAppTask); - - // Replace the mocked drag pending intent and ensure it resolves to the same activity - PendingIntent launchIntent = mock(PendingIntent.class); - ResolveInfo launchInfo = new ResolveInfo(); - launchInfo.activityInfo = mFullscreenAppTask.topActivityInfo; - doReturn(Collections.singletonList(launchInfo)) - .when(launchIntent).queryIntentComponents(anyInt()); - mActivityClipData.getItemAt(0).getIntent().putExtra(ClipDescription.EXTRA_PENDING_INTENT, - launchIntent); - - doReturn(true).when(mSplitScreenStarter).isSplitScreenVisible(); - doReturn(mFullscreenAppTask).when(mSplitScreenStarter).getTaskInfo(anyInt()); - mPolicy.start(mLandscapeDisplayLayout, mActivityClipData, mLoggerSessionId); - Intent fillInIntent = mPolicy.getStartIntentFillInIntent(launchIntent, 0); - assertTrue((fillInIntent.getFlags() & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) != 0); - } - private Target filterTargetByType(ArrayList<Target> targets, int type) { for (Target t : targets) { if (type == t.type) { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java index b976c1287aca..dcc504ad0cdb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutControllerTest.java @@ -16,10 +16,11 @@ package com.android.wm.shell.hidedisplaycutout; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import android.platform.test.annotations.Presubmit; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; @@ -27,7 +28,8 @@ import android.testing.TestableLooper; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; -import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.sysui.ShellController; import org.junit.Before; import org.junit.Test; @@ -35,25 +37,30 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -@Presubmit @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class HideDisplayCutoutControllerTest { +public class HideDisplayCutoutControllerTest extends ShellTestCase { private TestableContext mContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getTargetContext(), null); - private HideDisplayCutoutController mHideDisplayCutoutController; @Mock - private HideDisplayCutoutOrganizer mMockDisplayAreaOrganizer; + private ShellController mShellController; @Mock - private ShellExecutor mMockMainExecutor; + private HideDisplayCutoutOrganizer mMockDisplayAreaOrganizer; + + private HideDisplayCutoutController mHideDisplayCutoutController; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mHideDisplayCutoutController = new HideDisplayCutoutController( - mContext, mMockDisplayAreaOrganizer, mMockMainExecutor); + mContext, mShellController, mMockDisplayAreaOrganizer); + } + + @Test + public void instantiateController_registerConfigChangeListener() { + verify(mShellController, times(1)).addConfigurationChangeListener(any()); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java index 16e92395c85e..49521cfbb34d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutoutOrganizerTest.java @@ -32,7 +32,6 @@ import android.content.res.Configuration; import android.graphics.Insets; import android.graphics.Rect; import android.os.Binder; -import android.platform.test.annotations.Presubmit; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; @@ -48,6 +47,7 @@ import android.window.WindowContainerToken; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; @@ -61,11 +61,10 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; -@Presubmit @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class HideDisplayCutoutOrganizerTest { +public class HideDisplayCutoutOrganizerTest extends ShellTestCase { private TestableContext mContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getTargetContext(), null); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java index 1eadeed7cd3b..184a8dfecff9 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/kidsmode/KidsModeTaskOrganizerTest.java @@ -44,6 +44,7 @@ import android.window.WindowContainerTransaction; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayInsetsController; import com.android.wm.shell.common.ShellExecutor; @@ -61,7 +62,7 @@ import java.util.Optional; @SmallTest @RunWith(AndroidJUnit4.class) -public class KidsModeTaskOrganizerTest { +public class KidsModeTaskOrganizerTest extends ShellTestCase { @Mock private ITaskOrganizerController mTaskOrganizerController; @Mock private Context mContext; @Mock private Handler mHandler; 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 6a6db8aa3c04..dbf93ae35c18 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 @@ -30,10 +30,10 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.om.IOverlayManager; import android.graphics.Rect; import android.os.Handler; import android.os.UserHandle; @@ -41,16 +41,15 @@ import android.testing.AndroidTestingRunner; import android.util.ArrayMap; import android.view.Display; import android.view.Surface; -import android.view.SurfaceControl; import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; -import com.android.internal.jank.InteractionJankMonitor; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.sysui.ShellController; import org.junit.Before; import org.junit.Test; @@ -71,6 +70,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { OneHandedState mSpiedTransitionState; @Mock + ShellController mMockShellController; + @Mock DisplayLayout mDisplayLayout; @Mock DisplayController mMockDisplayController; @@ -87,16 +88,10 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Mock OneHandedUiEventLogger mMockUiEventLogger; @Mock - InteractionJankMonitor mMockJankMonitor; - @Mock - IOverlayManager mMockOverlayManager; - @Mock TaskStackListenerImpl mMockTaskStackListener; @Mock ShellExecutor mMockShellMainExecutor; @Mock - SurfaceControl mMockLeash; - @Mock Handler mMockShellMainHandler; final boolean mDefaultEnabled = true; @@ -132,6 +127,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext); mSpiedOneHandedController = spy(new OneHandedController( mContext, + mMockShellController, mMockDisplayController, mMockDisplayAreaOrganizer, mMockTouchHandler, @@ -140,9 +136,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { mOneHandedAccessibilityUtil, mSpiedTimeoutHandler, mSpiedTransitionState, - mMockJankMonitor, mMockUiEventLogger, - mMockOverlayManager, mMockTaskStackListener, mMockShellMainExecutor, mMockShellMainHandler) @@ -150,6 +144,16 @@ public class OneHandedControllerTest extends OneHandedTestCase { } @Test + public void testControllerRegistersConfigChangeListener() { + verify(mMockShellController, times(1)).addConfigurationChangeListener(any()); + } + + @Test + public void testControllerRegistersKeyguardChangeListener() { + verify(mMockShellController, times(1)).addKeyguardChangeListener(any()); + } + + @Test public void testDefaultShouldNotInOneHanded() { // Assert default transition state is STATE_NONE assertThat(mSpiedTransitionState.getState()).isEqualTo(STATE_NONE); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java index dba1b8b86261..e6a8220e081b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedStateTest.java @@ -29,22 +29,19 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.om.IOverlayManager; import android.graphics.Rect; import android.os.Handler; -import android.os.UserHandle; import android.testing.AndroidTestingRunner; import android.util.ArrayMap; import android.view.Display; -import android.view.SurfaceControl; import androidx.test.filters.SmallTest; -import com.android.internal.jank.InteractionJankMonitor; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayLayout; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.sysui.ShellController; import org.junit.Before; import org.junit.Test; @@ -55,7 +52,6 @@ import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) public class OneHandedStateTest extends OneHandedTestCase { - private int mCurrentUser = UserHandle.myUserId(); Display mDisplay; DisplayLayout mDisplayLayout; @@ -65,6 +61,8 @@ public class OneHandedStateTest extends OneHandedTestCase { OneHandedState mSpiedState; @Mock + ShellController mMockShellController; + @Mock DisplayController mMockDisplayController; @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; @@ -77,16 +75,10 @@ public class OneHandedStateTest extends OneHandedTestCase { @Mock OneHandedUiEventLogger mMockUiEventLogger; @Mock - InteractionJankMonitor mMockJankMonitor; - @Mock - IOverlayManager mMockOverlayManager; - @Mock TaskStackListenerImpl mMockTaskStackListener; @Mock ShellExecutor mMockShellMainExecutor; @Mock - SurfaceControl mMockLeash; - @Mock Handler mMockShellMainHandler; final boolean mDefaultEnabled = true; @@ -119,6 +111,7 @@ public class OneHandedStateTest extends OneHandedTestCase { mOneHandedAccessibilityUtil = new OneHandedAccessibilityUtil(mContext); mSpiedOneHandedController = spy(new OneHandedController( mContext, + mMockShellController, mMockDisplayController, mMockDisplayAreaOrganizer, mMockTouchHandler, @@ -127,9 +120,7 @@ public class OneHandedStateTest extends OneHandedTestCase { mOneHandedAccessibilityUtil, mSpiedTimeoutHandler, mSpiedState, - mMockJankMonitor, mMockUiEventLogger, - mMockOverlayManager, mMockTaskStackListener, mMockShellMainExecutor, mMockShellMainHandler) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java index 8b03dc58c3bf..808ab2167dc7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTestCase.java @@ -30,6 +30,8 @@ import android.view.WindowManager; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.wm.shell.ShellTestCase; + import org.junit.Before; import org.junit.Rule; import org.mockito.Answers; @@ -38,7 +40,7 @@ import org.mockito.Mock; /** * Base class that does One Handed specific setup. */ -public abstract class OneHandedTestCase { +public abstract class OneHandedTestCase extends ShellTestCase { @Mock(answer = Answers.RETURNS_DEEP_STUBS) protected Context mContext; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java index c685fdc1f09c..52d78ca7a004 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java @@ -21,6 +21,7 @@ import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; +import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; @@ -37,6 +38,7 @@ import android.view.SurfaceControl; import androidx.test.filters.SmallTest; +import com.android.wm.shell.MockSurfaceControlHelper; import com.android.wm.shell.ShellTestCase; import org.junit.Before; @@ -103,7 +105,8 @@ public class PipAnimationControllerTest extends ShellTestCase { final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue1, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); - oldAnimator.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new); + oldAnimator.setSurfaceControlTransactionFactory( + MockSurfaceControlHelper::createMockSurfaceControlTransaction); oldAnimator.start(); final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController @@ -133,7 +136,7 @@ public class PipAnimationControllerTest extends ShellTestCase { @Test public void pipTransitionAnimator_rotatedEndValue() { - final PipDummySurfaceControlTx tx = new PipDummySurfaceControlTx(); + final SurfaceControl.Transaction tx = createMockSurfaceControlTransaction(); final Rect startBounds = new Rect(200, 700, 400, 800); final Rect endBounds = new Rect(0, 0, 500, 1000); // Fullscreen to PiP. @@ -183,7 +186,8 @@ public class PipAnimationControllerTest extends ShellTestCase { final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController .getAnimator(mTaskInfo, mLeash, baseValue, startValue, endValue, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_0); - animator.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new); + animator.setSurfaceControlTransactionFactory( + MockSurfaceControlHelper::createMockSurfaceControlTransaction); animator.setPipAnimationCallback(mPipAnimationCallback); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipDummySurfaceControlTx.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipDummySurfaceControlTx.java deleted file mode 100644 index ccf8f6e03844..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipDummySurfaceControlTx.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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; - -import android.graphics.Matrix; -import android.view.SurfaceControl; - -/** - * A dummy {@link SurfaceControl.Transaction} class for testing purpose and supports - * method chaining. - */ -public class PipDummySurfaceControlTx extends SurfaceControl.Transaction { - @Override - public SurfaceControl.Transaction setAlpha(SurfaceControl leash, float alpha) { - return this; - } - - @Override - public SurfaceControl.Transaction setPosition(SurfaceControl leash, float x, float y) { - return this; - } - - @Override - public SurfaceControl.Transaction setWindowCrop(SurfaceControl leash, int w, int h) { - return this; - } - - @Override - public SurfaceControl.Transaction setCornerRadius(SurfaceControl leash, float radius) { - return this; - } - - @Override - public SurfaceControl.Transaction setShadowRadius(SurfaceControl leash, float radius) { - return this; - } - - @Override - public SurfaceControl.Transaction setMatrix(SurfaceControl leash, Matrix matrix, - float[] float9) { - return this; - } - - @Override - public SurfaceControl.Transaction setFrameTimelineVsync(long frameTimelineVsyncId) { - return this; - } - - @Override - public void apply() {} -} - diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index e8e6254697c2..b351f8fcf838 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -44,6 +44,7 @@ import android.util.Size; import android.view.DisplayInfo; import android.window.WindowContainerToken; +import com.android.wm.shell.MockSurfaceControlHelper; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; @@ -246,7 +247,8 @@ public class PipTaskOrganizerTest extends ShellTestCase { mPipBoundsState.setDisplayLayout(new DisplayLayout(info, mContext.getResources(), true, true)); mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA); - mSpiedPipTaskOrganizer.setSurfaceControlTransactionFactory(PipDummySurfaceControlTx::new); + mSpiedPipTaskOrganizer.setSurfaceControlTransactionFactory( + MockSurfaceControlHelper::createMockSurfaceControlTransaction); doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong()); doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any()); } 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 babc9707ef9c..f192514c37ab 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 @@ -24,6 +24,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -54,6 +55,7 @@ import com.android.wm.shell.pip.PipSnapAlgorithm; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipTransitionState; +import com.android.wm.shell.sysui.ShellController; import org.junit.Before; import org.junit.Test; @@ -73,6 +75,7 @@ import java.util.Set; public class PipControllerTest extends ShellTestCase { private PipController mPipController; + @Mock private ShellController mMockShellController; @Mock private DisplayController mMockDisplayController; @Mock private PhonePipMenuController mMockPhonePipMenuController; @Mock private PipAppOpsListener mMockPipAppOpsListener; @@ -102,7 +105,7 @@ public class PipControllerTest extends ShellTestCase { ((Runnable) invocation.getArgument(0)).run(); return null; }).when(mMockExecutor).execute(any()); - mPipController = new PipController(mContext, mMockDisplayController, + mPipController = new PipController(mContext, mMockShellController, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm, mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController, @@ -115,6 +118,16 @@ public class PipControllerTest extends ShellTestCase { } @Test + public void instantiatePipController_registerConfigChangeListener() { + verify(mMockShellController, times(1)).addConfigurationChangeListener(any()); + } + + @Test + public void instantiatePipController_registerKeyguardChangeListener() { + verify(mMockShellController, times(1)).addKeyguardChangeListener(any()); + } + + @Test public void instantiatePipController_registersPipTransitionCallback() { verify(mMockPipTransitionController).registerPipTransitionCallback(any()); } @@ -136,7 +149,7 @@ public class PipControllerTest extends ShellTestCase { when(mockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false); when(spyContext.getPackageManager()).thenReturn(mockPackageManager); - assertNull(PipController.create(spyContext, mMockDisplayController, + assertNull(PipController.create(spyContext, mMockShellController, mMockDisplayController, mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm, mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java index 068a60a12f5f..50d02ae0dccd 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/SplitBoundsTest.java @@ -9,6 +9,7 @@ import android.graphics.Rect; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.util.SplitBounds; import org.junit.Before; @@ -17,7 +18,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) @SmallTest -public class SplitBoundsTest { +public class SplitBoundsTest extends ShellTestCase { private static final int DEVICE_WIDTH = 100; private static final int DEVICE_LENGTH = 200; private static final int DIVIDER_SIZE = 20; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java new file mode 100644 index 000000000000..c7a261f32e43 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java @@ -0,0 +1,167 @@ +/* + * 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.splitscreen; + +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 com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; + +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.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ActivityInfo; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.RootTaskDisplayAreaOrganizer; +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.DisplayImeController; +import com.android.wm.shell.common.DisplayInsetsController; +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.TransactionPool; +import com.android.wm.shell.recents.RecentTasksController; +import com.android.wm.shell.sysui.ShellController; +import com.android.wm.shell.transition.Transitions; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Optional; + +/** + * Tests for {@link SplitScreenController} + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class SplitScreenControllerTests extends ShellTestCase { + + @Mock ShellController mShellController; + @Mock ShellTaskOrganizer mTaskOrganizer; + @Mock SyncTransactionQueue mSyncQueue; + @Mock RootTaskDisplayAreaOrganizer mRootTDAOrganizer; + @Mock ShellExecutor mMainExecutor; + @Mock DisplayController mDisplayController; + @Mock DisplayImeController mDisplayImeController; + @Mock DisplayInsetsController mDisplayInsetsController; + @Mock Transitions mTransitions; + @Mock TransactionPool mTransactionPool; + @Mock IconProvider mIconProvider; + @Mock Optional<RecentTasksController> mRecentTasks; + + private SplitScreenController mSplitScreenController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mSplitScreenController = spy(new SplitScreenController(mShellController, mTaskOrganizer, + mSyncQueue, mContext, mRootTDAOrganizer, mMainExecutor, mDisplayController, + mDisplayImeController, mDisplayInsetsController, mTransitions, mTransactionPool, + mIconProvider, mRecentTasks)); + } + + @Test + public void testControllerRegistersKeyguardChangeListener() { + when(mDisplayController.getDisplayLayout(anyInt())).thenReturn(new DisplayLayout()); + mSplitScreenController.onOrganizerRegistered(); + verify(mShellController, times(1)).addKeyguardChangeListener(any()); + } + + @Test + public void testIsLaunchingAdjacently_notInSplitScreen() { + doReturn(false).when(mSplitScreenController).isSplitScreenVisible(); + doReturn(true).when(mSplitScreenController).isValidToEnterSplitScreen(any()); + + // Verify launching the same activity returns true. + Intent startIntent = createStartIntent("startActivity"); + ActivityManager.RunningTaskInfo focusTaskInfo = + createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, startIntent); + doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo(); + assertTrue(mSplitScreenController.isLaunchingAdjacently( + startIntent, SPLIT_POSITION_TOP_OR_LEFT)); + + // Verify launching different activity returns false. + Intent diffIntent = createStartIntent("diffActivity"); + focusTaskInfo = + createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, diffIntent); + doReturn(focusTaskInfo).when(mSplitScreenController).getFocusingTaskInfo(); + assertFalse(mSplitScreenController.isLaunchingAdjacently( + startIntent, SPLIT_POSITION_TOP_OR_LEFT)); + } + + @Test + public void testIsLaunchingAdjacently_inSplitScreen() { + doReturn(true).when(mSplitScreenController).isSplitScreenVisible(); + + // Verify launching the same activity returns true. + Intent startIntent = createStartIntent("startActivity"); + ActivityManager.RunningTaskInfo pairingTaskInfo = + createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, startIntent); + doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt()); + assertTrue(mSplitScreenController.isLaunchingAdjacently( + startIntent, SPLIT_POSITION_TOP_OR_LEFT)); + + // Verify launching different activity returns false. + Intent diffIntent = createStartIntent("diffActivity"); + pairingTaskInfo = + createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, diffIntent); + doReturn(pairingTaskInfo).when(mSplitScreenController).getTaskInfo(anyInt()); + assertFalse(mSplitScreenController.isLaunchingAdjacently( + startIntent, SPLIT_POSITION_TOP_OR_LEFT)); + } + + private Intent createStartIntent(String activityName) { + Intent intent = new Intent(); + intent.setComponent(new ComponentName(mContext, activityName)); + return intent; + } + + private ActivityManager.RunningTaskInfo createTaskInfo(int winMode, int actType, + Intent strIntent) { + ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); + info.configuration.windowConfiguration.setActivityType(actType); + info.configuration.windowConfiguration.setWindowingMode(winMode); + info.supportsMultiWindow = true; + info.baseIntent = strIntent; + info.baseActivity = strIntent.getComponent(); + ActivityInfo activityInfo = new ActivityInfo(); + activityInfo.packageName = info.baseActivity.getPackageName(); + activityInfo.name = info.baseActivity.getClassName(); + info.topActivityInfo = activityInfo; + return info; + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index af2c495c85c5..4b68870d4129 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -127,6 +127,9 @@ public class StageCoordinatorTests extends ShellTestCase { mRootTask = new TestRunningTaskInfoBuilder().build(); mRootLeash = new SurfaceControl.Builder(mSurfaceSession).setName("test").build(); mStageCoordinator.onTaskAppeared(mRootTask, mRootLeash); + + mSideStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build(); + mMainStage.mRootTaskInfo = new TestRunningTaskInfoBuilder().build(); } @Test @@ -224,8 +227,8 @@ public class StageCoordinatorTests extends ShellTestCase { mStageCoordinator.exitSplitScreen(testTaskId, EXIT_REASON_RETURN_HOME); verify(mMainStage).reorderChild(eq(testTaskId), eq(true), any(WindowContainerTransaction.class)); - verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(false)); - verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(true)); + verify(mSideStage).dismiss(any(WindowContainerTransaction.class), eq(false)); + verify(mMainStage).resetBounds(any(WindowContainerTransaction.class)); } @Test @@ -237,8 +240,8 @@ public class StageCoordinatorTests extends ShellTestCase { mStageCoordinator.exitSplitScreen(testTaskId, EXIT_REASON_RETURN_HOME); verify(mSideStage).reorderChild(eq(testTaskId), eq(true), any(WindowContainerTransaction.class)); - verify(mSideStage).removeAllTasks(any(WindowContainerTransaction.class), eq(true)); - verify(mMainStage).deactivate(any(WindowContainerTransaction.class), eq(false)); + verify(mSideStage).resetBounds(any(WindowContainerTransaction.class)); + verify(mMainStage).dismiss(any(WindowContainerTransaction.class), eq(false)); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java index 14d8ce4682c6..46b040fd4325 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java @@ -73,6 +73,7 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.launcher3.icons.IconProvider; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.HandlerExecutor; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; @@ -91,7 +92,7 @@ import java.util.function.IntSupplier; */ @SmallTest @RunWith(AndroidJUnit4.class) -public class StartingSurfaceDrawerTests { +public class StartingSurfaceDrawerTests extends ShellTestCase { @Mock private IBinder mBinder; @Mock diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java index 78e27c956807..3de50bb60470 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/TaskSnapshotWindowTest.java @@ -47,6 +47,7 @@ import android.window.TaskSnapshot; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import org.junit.Test; @@ -58,7 +59,7 @@ import org.junit.runner.RunWith; */ @SmallTest @RunWith(AndroidJUnit4.class) -public class TaskSnapshotWindowTest { +public class TaskSnapshotWindowTest extends ShellTestCase { private TaskSnapshotWindow mWindow; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java new file mode 100644 index 000000000000..1c0e46f7264e --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java @@ -0,0 +1,331 @@ +/* + * 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.sysui; + +import static org.junit.Assert.assertTrue; + +import android.content.res.Configuration; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.common.ShellExecutor; + +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; + +import java.util.Locale; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class ShellControllerTest extends ShellTestCase { + + @Mock + private ShellExecutor mExecutor; + + private ShellController mController; + private TestConfigurationChangeListener mConfigChangeListener; + private TestKeyguardChangeListener mKeyguardChangeListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mKeyguardChangeListener = new TestKeyguardChangeListener(); + mConfigChangeListener = new TestConfigurationChangeListener(); + mController = new ShellController(mExecutor); + mController.onConfigurationChanged(getConfigurationCopy()); + } + + @After + public void tearDown() { + // Do nothing + } + + @Test + public void testAddKeyguardChangeListener_ensureCallback() { + mController.addKeyguardChangeListener(mKeyguardChangeListener); + + mController.onKeyguardVisibilityChanged(true, false, false); + assertTrue(mKeyguardChangeListener.visibilityChanged == 1); + assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); + } + + @Test + public void testDoubleAddKeyguardChangeListener_ensureSingleCallback() { + mController.addKeyguardChangeListener(mKeyguardChangeListener); + mController.addKeyguardChangeListener(mKeyguardChangeListener); + + mController.onKeyguardVisibilityChanged(true, false, false); + assertTrue(mKeyguardChangeListener.visibilityChanged == 1); + assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); + } + + @Test + public void testAddRemoveKeyguardChangeListener_ensureNoCallback() { + mController.addKeyguardChangeListener(mKeyguardChangeListener); + mController.removeKeyguardChangeListener(mKeyguardChangeListener); + + mController.onKeyguardVisibilityChanged(true, false, false); + assertTrue(mKeyguardChangeListener.visibilityChanged == 0); + assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); + } + + @Test + public void testKeyguardVisibilityChanged() { + mController.addKeyguardChangeListener(mKeyguardChangeListener); + + mController.onKeyguardVisibilityChanged(true, true, true); + assertTrue(mKeyguardChangeListener.visibilityChanged == 1); + assertTrue(mKeyguardChangeListener.lastAnimatingDismiss); + assertTrue(mKeyguardChangeListener.lastOccluded); + assertTrue(mKeyguardChangeListener.lastAnimatingDismiss); + assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 0); + } + + @Test + public void testKeyguardDismissAnimationFinished() { + mController.addKeyguardChangeListener(mKeyguardChangeListener); + + mController.onKeyguardDismissAnimationFinished(); + assertTrue(mKeyguardChangeListener.visibilityChanged == 0); + assertTrue(mKeyguardChangeListener.dismissAnimationFinished == 1); + } + + @Test + public void testAddConfigurationChangeListener_ensureCallback() { + mController.addConfigurationChangeListener(mConfigChangeListener); + + Configuration newConfig = getConfigurationCopy(); + newConfig.densityDpi = 200; + mController.onConfigurationChanged(newConfig); + assertTrue(mConfigChangeListener.configChanges == 1); + } + + @Test + public void testDoubleAddConfigurationChangeListener_ensureSingleCallback() { + mController.addConfigurationChangeListener(mConfigChangeListener); + mController.addConfigurationChangeListener(mConfigChangeListener); + + Configuration newConfig = getConfigurationCopy(); + newConfig.densityDpi = 200; + mController.onConfigurationChanged(newConfig); + assertTrue(mConfigChangeListener.configChanges == 1); + } + + @Test + public void testAddRemoveConfigurationChangeListener_ensureNoCallback() { + mController.addConfigurationChangeListener(mConfigChangeListener); + mController.removeConfigurationChangeListener(mConfigChangeListener); + + Configuration newConfig = getConfigurationCopy(); + newConfig.densityDpi = 200; + mController.onConfigurationChanged(newConfig); + assertTrue(mConfigChangeListener.configChanges == 0); + } + + @Test + public void testMultipleConfigurationChangeListeners() { + TestConfigurationChangeListener listener2 = new TestConfigurationChangeListener(); + mController.addConfigurationChangeListener(mConfigChangeListener); + mController.addConfigurationChangeListener(listener2); + + Configuration newConfig = getConfigurationCopy(); + newConfig.densityDpi = 200; + mController.onConfigurationChanged(newConfig); + assertTrue(mConfigChangeListener.configChanges == 1); + assertTrue(listener2.configChanges == 1); + } + + @Test + public void testRemoveListenerDuringCallback() { + TestConfigurationChangeListener badListener = new TestConfigurationChangeListener() { + @Override + public void onConfigurationChanged(Configuration newConfiguration) { + mController.removeConfigurationChangeListener(this); + } + }; + mController.addConfigurationChangeListener(badListener); + mController.addConfigurationChangeListener(mConfigChangeListener); + + // Ensure we don't fail just because a listener was removed mid-callback + Configuration newConfig = getConfigurationCopy(); + newConfig.densityDpi = 200; + mController.onConfigurationChanged(newConfig); + } + + @Test + public void testDensityChangeCallback() { + mController.addConfigurationChangeListener(mConfigChangeListener); + + Configuration newConfig = getConfigurationCopy(); + newConfig.densityDpi = 200; + mController.onConfigurationChanged(newConfig); + assertTrue(mConfigChangeListener.configChanges == 1); + assertTrue(mConfigChangeListener.densityChanges == 1); + assertTrue(mConfigChangeListener.smallestWidthChanges == 0); + assertTrue(mConfigChangeListener.themeChanges == 0); + assertTrue(mConfigChangeListener.localeChanges == 0); + } + + @Test + public void testFontScaleChangeCallback() { + mController.addConfigurationChangeListener(mConfigChangeListener); + + Configuration newConfig = getConfigurationCopy(); + newConfig.fontScale = 2; + mController.onConfigurationChanged(newConfig); + assertTrue(mConfigChangeListener.configChanges == 1); + assertTrue(mConfigChangeListener.densityChanges == 1); + assertTrue(mConfigChangeListener.smallestWidthChanges == 0); + assertTrue(mConfigChangeListener.themeChanges == 0); + assertTrue(mConfigChangeListener.localeChanges == 0); + } + + @Test + public void testSmallestWidthChangeCallback() { + mController.addConfigurationChangeListener(mConfigChangeListener); + + Configuration newConfig = getConfigurationCopy(); + newConfig.smallestScreenWidthDp = 100; + mController.onConfigurationChanged(newConfig); + assertTrue(mConfigChangeListener.configChanges == 1); + assertTrue(mConfigChangeListener.densityChanges == 0); + assertTrue(mConfigChangeListener.smallestWidthChanges == 1); + assertTrue(mConfigChangeListener.themeChanges == 0); + assertTrue(mConfigChangeListener.localeChanges == 0); + } + + @Test + public void testThemeChangeCallback() { + mController.addConfigurationChangeListener(mConfigChangeListener); + + Configuration newConfig = getConfigurationCopy(); + newConfig.assetsSeq++; + mController.onConfigurationChanged(newConfig); + assertTrue(mConfigChangeListener.configChanges == 1); + assertTrue(mConfigChangeListener.densityChanges == 0); + assertTrue(mConfigChangeListener.smallestWidthChanges == 0); + assertTrue(mConfigChangeListener.themeChanges == 1); + assertTrue(mConfigChangeListener.localeChanges == 0); + } + + @Test + public void testNightModeChangeCallback() { + mController.addConfigurationChangeListener(mConfigChangeListener); + + Configuration newConfig = getConfigurationCopy(); + newConfig.uiMode = Configuration.UI_MODE_NIGHT_YES; + mController.onConfigurationChanged(newConfig); + assertTrue(mConfigChangeListener.configChanges == 1); + assertTrue(mConfigChangeListener.densityChanges == 0); + assertTrue(mConfigChangeListener.smallestWidthChanges == 0); + assertTrue(mConfigChangeListener.themeChanges == 1); + assertTrue(mConfigChangeListener.localeChanges == 0); + } + + @Test + public void testLocaleChangeCallback() { + mController.addConfigurationChangeListener(mConfigChangeListener); + + Configuration newConfig = getConfigurationCopy(); + // Just change the locales to be different + if (newConfig.locale == Locale.CANADA) { + newConfig.locale = Locale.US; + } else { + newConfig.locale = Locale.CANADA; + } + mController.onConfigurationChanged(newConfig); + assertTrue(mConfigChangeListener.configChanges == 1); + assertTrue(mConfigChangeListener.densityChanges == 0); + assertTrue(mConfigChangeListener.smallestWidthChanges == 0); + assertTrue(mConfigChangeListener.themeChanges == 0); + assertTrue(mConfigChangeListener.localeChanges == 1); + } + + private Configuration getConfigurationCopy() { + final Configuration c = new Configuration(InstrumentationRegistry.getInstrumentation() + .getTargetContext().getResources().getConfiguration()); + // In tests this might be undefined so make sure it's valid + c.assetsSeq = 1; + return c; + } + + private class TestConfigurationChangeListener implements ConfigurationChangeListener { + // Counts of number of times each of the callbacks are called + public int configChanges; + public int densityChanges; + public int smallestWidthChanges; + public int themeChanges; + public int localeChanges; + + @Override + public void onConfigurationChanged(Configuration newConfiguration) { + configChanges++; + } + + @Override + public void onDensityOrFontScaleChanged() { + densityChanges++; + } + + @Override + public void onSmallestScreenWidthChanged() { + smallestWidthChanges++; + } + + @Override + public void onThemeChanged() { + themeChanges++; + } + + @Override + public void onLocaleOrLayoutDirectionChanged() { + localeChanges++; + } + } + + private class TestKeyguardChangeListener implements KeyguardChangeListener { + // Counts of number of times each of the callbacks are called + public int visibilityChanged; + public boolean lastVisibility; + public boolean lastOccluded; + public boolean lastAnimatingDismiss; + public int dismissAnimationFinished; + + @Override + public void onKeyguardVisibilityChanged(boolean visible, boolean occluded, + boolean animatingDismiss) { + lastVisibility = visible; + lastOccluded = occluded; + lastAnimatingDismiss = animatingDismiss; + visibilityChanged++; + } + + @Override + public void onKeyguardDismissAnimationFinished() { + dismissAnimationFinished++; + } + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java deleted file mode 100644 index d6142753b48a..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/tasksurfacehelper/TaskSurfaceHelperControllerTest.java +++ /dev/null @@ -1,58 +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.tasksurfacehelper; - -import static org.mockito.Mockito.verify; - -import android.platform.test.annotations.Presubmit; -import android.testing.AndroidTestingRunner; -import android.view.SurfaceControl; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.ShellExecutor; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@Presubmit -@RunWith(AndroidTestingRunner.class) -@SmallTest -public class TaskSurfaceHelperControllerTest { - private TaskSurfaceHelperController mTaskSurfaceHelperController; - @Mock - private ShellTaskOrganizer mMockTaskOrganizer; - @Mock - private ShellExecutor mMockShellExecutor; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - mTaskSurfaceHelperController = new TaskSurfaceHelperController( - mMockTaskOrganizer, mMockShellExecutor); - } - - @Test - public void testSetGameModeForTask() { - mTaskSurfaceHelperController.setGameModeForTask(/*taskId*/1, /*gameMode*/3); - verify(mMockTaskOrganizer).setSurfaceMetadata(1, SurfaceControl.METADATA_GAME_MODE, 3); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index a0b12976b467..e2f2b71cea04 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -79,6 +79,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; @@ -98,7 +99,7 @@ import java.util.ArrayList; */ @SmallTest @RunWith(AndroidJUnit4.class) -public class ShellTransitionTests { +public class ShellTransitionTests extends ShellTestCase { private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class); private final TransactionPool mTransactionPool = mock(TransactionPool.class); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java new file mode 100644 index 000000000000..680034bd2ea5 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java @@ -0,0 +1,178 @@ +/* + * 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.windowdecor; + +import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlBuilder; +import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.same; +import static org.mockito.Mockito.verify; + +import android.app.ActivityManager; +import android.content.Context; +import android.graphics.Color; +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.view.Display; +import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; +import android.view.View; +import android.window.WindowContainerTransaction; + +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 org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; + +import java.util.function.Supplier; + +/** + * Tests for {@link WindowDecoration}. + * + * Build/Install/Run: + * atest WMShellUnitTests:WindowDecorationTests + */ +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class WindowDecorationTests extends ShellTestCase { + private static final int CAPTION_HEIGHT_DP = 32; + private static final int SHADOW_RADIUS_DP = 5; + + private final Rect mOutsetsDp = new Rect(); + private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult = + new WindowDecoration.RelayoutResult<>(); + + @Mock + private DisplayController mMockDisplayController; + @Mock + private ShellTaskOrganizer mMockShellTaskOrganizer; + @Mock + private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory; + @Mock + private SurfaceControlViewHost mMockSurfaceControlViewHost; + @Mock + private TestView mMockView; + @Mock + private WindowContainerTransaction mMockWindowContainerTransaction; + + private SurfaceControl.Builder mMockSurfaceControlBuilder; + private SurfaceControl.Transaction mMockSurfaceControlTransaction; + + @Before + public void setUp() { + mMockSurfaceControlBuilder = createMockSurfaceControlBuilder(mock(SurfaceControl.class)); + mMockSurfaceControlTransaction = createMockSurfaceControlTransaction(); + + doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory) + .create(any(), any(), any(), anyBoolean()); + } + + @Test + public void testNotCrashWhenDisplayAppearsAfterTask() { + doReturn(mock(Display.class)).when(mMockDisplayController) + .getDisplay(Display.DEFAULT_DISPLAY); + + final int displayId = Display.DEFAULT_DISPLAY + 1; + final ActivityManager.TaskDescription.Builder taskDescriptionBuilder = + new ActivityManager.TaskDescription.Builder() + .setBackgroundColor(Color.BLACK); + final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder() + .setDisplayId(displayId) + .setTaskDescriptionBuilder(taskDescriptionBuilder) + .setVisible(true) + .build(); + + final TestWindowDecoration windowDecor = + createWindowDecoration(taskInfo, new SurfaceControl()); + windowDecor.relayout(taskInfo); + + // It shouldn't show the window decoration when it can't obtain the display instance. + assertThat(mRelayoutResult.mRootView).isNull(); + + final ArgumentCaptor<DisplayController.OnDisplaysChangedListener> listenerArgumentCaptor = + ArgumentCaptor.forClass(DisplayController.OnDisplaysChangedListener.class); + verify(mMockDisplayController).addDisplayWindowListener(listenerArgumentCaptor.capture()); + final DisplayController.OnDisplaysChangedListener listener = + listenerArgumentCaptor.getValue(); + + // Adding an irrelevant display shouldn't change the result. + listener.onDisplayAdded(Display.DEFAULT_DISPLAY); + assertThat(mRelayoutResult.mRootView).isNull(); + + final Display mockDisplay = mock(Display.class); + doReturn(mockDisplay).when(mMockDisplayController).getDisplay(displayId); + + listener.onDisplayAdded(displayId); + + // The listener should be removed when the display shows up. + verify(mMockDisplayController).removeDisplayWindowListener(same(listener)); + + assertThat(mRelayoutResult.mRootView).isSameInstanceAs(mMockView); + verify(mMockSurfaceControlViewHostFactory) + .create(any(), eq(mockDisplay), any(), anyBoolean()); + verify(mMockSurfaceControlViewHost).setView(same(mMockView), any()); + } + + private TestWindowDecoration createWindowDecoration( + ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) { + return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer, + taskInfo, testSurface, () -> mMockSurfaceControlBuilder, + mMockSurfaceControlViewHostFactory); + } + + private static class TestView extends View implements TaskFocusStateConsumer { + private TestView(Context context) { + super(context); + } + + @Override + public void setTaskFocusState(boolean focused) {} + } + + private class TestWindowDecoration extends WindowDecoration<TestView> { + TestWindowDecoration(Context context, DisplayController displayController, + ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl taskSurface, + Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier, + SurfaceControlViewHostFactory surfaceControlViewHostFactory) { + super(context, displayController, taskOrganizer, taskInfo, taskSurface, + surfaceControlBuilderSupplier, surfaceControlViewHostFactory); + } + + @Override + void relayout(ActivityManager.RunningTaskInfo taskInfo) { + relayout(null /* taskInfo */, 0 /* layoutResId */, mMockView, CAPTION_HEIGHT_DP, + mOutsetsDp, SHADOW_RADIUS_DP, mMockSurfaceControlTransaction, + mMockWindowContainerTransaction, mRelayoutResult); + } + } +} diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 4cce87ad1a2f..a3ba88e4ee8a 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -90,11 +90,36 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec SkRect srcRect = inSrcRect.toSkRect(); - SkRect imageSrcRect = - SkRect::MakeLTRB(cropRect.left, cropRect.top, cropRect.right, cropRect.bottom); - if (imageSrcRect.isEmpty()) { - imageSrcRect = SkRect::MakeIWH(description.width, description.height); + SkRect imageSrcRect = SkRect::MakeIWH(description.width, description.height); + SkISize imageWH = SkISize::Make(description.width, description.height); + if (cropRect.left < cropRect.right && cropRect.top < cropRect.bottom) { + imageSrcRect = + SkRect::MakeLTRB(cropRect.left, cropRect.top, cropRect.right, cropRect.bottom); + imageWH = SkISize::Make(cropRect.right - cropRect.left, cropRect.bottom - cropRect.top); + + // Chroma channels of YUV420 images are subsampled we may need to shrink the crop region by + // a whole texel on each side. Since skia still adds its own 0.5 inset, we apply an + // additional 0.5 inset. See GLConsumer::computeTransformMatrix for details. + float shrinkAmount = 0.0f; + switch (description.format) { + // Use HAL formats since some AHB formats are only available in vndk + case HAL_PIXEL_FORMAT_YCBCR_420_888: + case HAL_PIXEL_FORMAT_YV12: + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + shrinkAmount = 0.5f; + break; + default: + break; + } + + // Shrink the crop if it has more than 1-px and differs from the buffer size. + if (imageWH.width() > 1 && imageWH.width() < (int32_t)description.width) + imageSrcRect = imageSrcRect.makeInset(shrinkAmount, 0); + + if (imageWH.height() > 1 && imageWH.height() < (int32_t)description.height) + imageSrcRect = imageSrcRect.makeInset(0, shrinkAmount); } + ALOGV("imageSrcRect = " RECT_STRING, SK_RECT_ARGS(imageSrcRect)); // Represents the "logical" width/height of the texture. That is, the dimensions of the buffer @@ -153,7 +178,7 @@ CopyResult Readback::copySurfaceInto(ANativeWindow* window, const Rect& inSrcRec */ SkMatrix m; - const SkRect imageDstRect = SkRect::MakeIWH(imageSrcRect.width(), imageSrcRect.height()); + const SkRect imageDstRect = SkRect::Make(imageWH); const float px = imageDstRect.centerX(); const float py = imageDstRect.centerY(); if (windowTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) { diff --git a/location/java/android/location/SatellitePvt.java b/location/java/android/location/SatellitePvt.java index f3e15084d730..2031929514f3 100644 --- a/location/java/android/location/SatellitePvt.java +++ b/location/java/android/location/SatellitePvt.java @@ -539,7 +539,7 @@ public final class SatellitePvt implements Parcelable { * * <p>This field is valid if {@link #hasIssueOfDataEphemeris()} is true. */ - @IntRange(from = 0, to = 255) + @IntRange(from = 0, to = 1023) public int getIssueOfDataEphemeris() { return mIssueOfDataEphemeris; } @@ -847,8 +847,8 @@ public final class SatellitePvt implements Parcelable { */ @NonNull public Builder setIssueOfDataEphemeris( - @IntRange(from = 0, to = 255) int issueOfDataEphemeris) { - Preconditions.checkArgumentInRange(issueOfDataEphemeris, 0, 255, + @IntRange(from = 0, to = 1023) int issueOfDataEphemeris) { + Preconditions.checkArgumentInRange(issueOfDataEphemeris, 0, 1023, "issueOfDataEphemeris"); mIssueOfDataEphemeris = issueOfDataEphemeris; mFlags = (byte) (mFlags | HAS_ISSUE_OF_DATA_EPHEMERIS); diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING index 4ec4767a35cc..05fbc7a52ec6 100644 --- a/media/TEST_MAPPING +++ b/media/TEST_MAPPING @@ -22,11 +22,15 @@ } ], "file_patterns": ["(?i)drm|crypto"] - } - ], - "imports": [ + }, { - "path": "frameworks/av/drm/mediadrm/plugins" + "name": "CtsMediaDrmFrameworkTestCases", + "options" : [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + } + ], + "file_patterns": ["(?i)drm|crypto"] } ] } diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml index 480bc484b73a..004b5632a18b 100644 --- a/packages/CompanionDeviceManager/res/values-af/strings.xml +++ b/packages/CompanionDeviceManager/res/values-af/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Stroom jou foon se programme"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Gee <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang tot hierdie inligting op jou foon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Oorkruistoestel-dienste"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toegang tot jou foon se foto\'s, media en kennisgewings"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toestemming om programme tussen jou toestelle te stroom"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Gee <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang tot hierdie inligting op jou foon"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Foto\'s en media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play Dienste"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toestemming om programme tussen jou toestelle te stroom"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> versoek tans namens jou <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toegang tot jou foon se foto\'s, media en kennisgewings"</string> <string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Laat toe"</string> diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml index 5f2bd7f1b3f9..b1f414414a15 100644 --- a/packages/CompanionDeviceManager/res/values-am/strings.xml +++ b/packages/CompanionDeviceManager/res/values-am/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"የስልክዎን መተግበሪያዎች በዥረት ይልቀቁ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ይህን መረጃ ከስልክዎ እንዲደርስበት ይፍቀዱለት"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"መሣሪያ ተሻጋሪ አገልግሎቶች"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> የእርስዎን ስልክ ፎቶዎች፣ ሚዲያ እና ማሳወቂያዎች ለመድረስ የእርስዎን <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ወክሎ ፈቃድ እየጠየቀ ነው"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ይህን መረጃ ከስልክዎ ላይ እንዲደርስ ይፍቀዱለት"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"ፎቶዎች እና ሚዲያ"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"የGoogle Play አገልግሎቶች"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> በእርስዎ መሣሪያዎች መካከል መተግበሪያዎችን በዥረት ለመልቀቅ የእርስዎን <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ወክሎ ፈቃድ እየጠየቀ ነው"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"ፍቀድ"</string> diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml index 138386b0b8e6..4268c0b909ee 100644 --- a/packages/CompanionDeviceManager/res/values-ar/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"بث تطبيقات هاتفك"</string> <string name="title_app_streaming" msgid="2270331024626446950">"السماح لتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بالوصول إلى هذه المعلومات من هاتفك"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"الخدمات التي تعمل بين الأجهزة"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"تطلب \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" الحصول على إذن نيابةً عن <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> للوصول إلى الصور والوسائط والإشعارات في هاتفك."</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"السماح لتطبيق <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> بالوصول إلى هذه المعلومات من هاتفك"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"الصور والوسائط"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"خدمات Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"يطلب التطبيق <xliff:g id="APP_NAME">%1$s</xliff:g> الحصول على إذن نيابةً عن <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> لمشاركة التطبيقات بين أجهزتك."</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"السماح"</string> diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml index 95c4e68c0dcf..644140035398 100644 --- a/packages/CompanionDeviceManager/res/values-as/strings.xml +++ b/packages/CompanionDeviceManager/res/values-as/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"আপোনাৰ ফ’নৰ এপ্ ষ্ট্ৰীম কৰক"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ক আপোনাৰ ফ’নৰ পৰা এই তথ্যখিনি এক্সেছ কৰাৰ অনুমতি দিয়ক"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ক্ৰছ-ডিভাইচ সেৱাসমূহ"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ হৈ আপোনাৰ ফ’নৰ ফট’, মিডিয়া আৰু জাননী এক্সেছ কৰাৰ বাবে অনুৰোধ জনাইছে"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ক আপোনাৰ ফ’নৰ পৰা এই তথ্যখিনি এক্সেছ কৰাৰ অনুমতি দিয়ক"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"ফট’ আৰু মিডিয়া"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play সেৱা"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ আপোনৰ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>ৰ হৈ আপোনাৰ ডিভাইচসমূহৰ মাজত এপ্ ষ্ট্ৰীম কৰাৰ বাবে অনুৰোধ জনাইছে"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিয়ক"</string> diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml index 9578fb5fcf47..3549317b383f 100644 --- a/packages/CompanionDeviceManager/res/values-az/strings.xml +++ b/packages/CompanionDeviceManager/res/values-az/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Telefonunuzun tətbiqlərini yayımlayın"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinə telefonunuzdan bu məlumata giriş icazəsi verin"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cihazlararası xidmətlər"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> adından telefonunuzun fotoları, mediası və bildirişlərinə giriş üçün icazə istəyir"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> adından cihazlarınız arasında tətbiqləri yayımlamaq üçün icazə istəyir"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tətbiqinə telefonunuzdan bu məlumata giriş icazəsi verin"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Foto və media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play xidmətləri"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> adından cihazlarınız arasında tətbiqləri yayımlamaq üçün icazə istəyir"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> tətbiqi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> adından telefonunuzun fotoları, mediası və bildirişlərinə giriş üçün icazə istəyir"</string> <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"İcazə verin"</string> diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml index d82124758ce6..75a4f1d2aeeb 100644 --- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Strimujte aplikacije na telefonu"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Dozvolite da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pristupa ovim informacijama sa telefona"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluge na više uređaja"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za pristup slikama, medijskom sadržaju i obaveštenjima sa telefona"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za strimovanje aplikacija između uređaja"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Dozvolite da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pristupa ovim informacijama sa telefona"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Slike i mediji"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play usluge"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za strimovanje aplikacija između uređaja"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahteva dozvolu u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za pristup slikama, medijskom sadržaju i obaveštenjima sa telefona"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string> diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml index 2086f2eddf09..82ff9ae00138 100644 --- a/packages/CompanionDeviceManager/res/values-be/strings.xml +++ b/packages/CompanionDeviceManager/res/values-be/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Трансліруйце змесціва праграм з вашага тэлефона"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> мець доступ да гэтай інфармацыі з вашага тэлефона"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сэрвісы для некалькіх прылад"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на доступ да фота, медыяфайлаў і апавяшчэнняў вашага тэлефона"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Дазвольце праграме <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> мець доступ да гэтай інфармацыі з вашага тэлефона"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Фота і медыяфайлы"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Сэрвісы Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на перадачу праграм плынню паміж вашымі прыладамі"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Дазволіць"</string> diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml index 134ae1c90bab..0154bfb7aac7 100644 --- a/packages/CompanionDeviceManager/res/values-bg/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Поточно предаване на приложенията на телефона ви"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Разрешете на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да осъществява достъп до тази информация от телефона ви"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Услуги за различни устройства"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ иска разрешение от името на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за достъп до снимките, мултимедията и известията на телефона ви"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Разрешете на <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да осъществява достъп до тази информация от телефона ви"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Снимки и мултимедия"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Услуги за Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> иска разрешение от името на <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> да предава поточно приложения между устройствата ви"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Разрешаване"</string> diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml index 08c4a16b9000..abdc12847bd3 100644 --- a/packages/CompanionDeviceManager/res/values-bn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"আপনার ফোনের অ্যাপ স্ট্রিমিংয়ের মাধ্যমে কাস্ট করুন"</string> <string name="title_app_streaming" msgid="2270331024626446950">"আপনার ফোন থেকে <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> অ্যাপকে এই তথ্য অ্যাক্সেস করার অনুমতি দিন"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ক্রস-ডিভাইস পরিষেবা"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"আপনার ফোনের ফটো, মিডিয়া এবং তথ্য অ্যাক্সেস করার জন্য <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এর হয়ে অনুমতি চাইছে"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"আপনার ফোন থেকে <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-কে এই তথ্য অ্যাক্সেস করার অনুমতি দিন"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"ফটো ও মিডিয়া"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play পরিষেবা"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"আপনার ডিভাইসগুলির মধ্যে অ্যাপ স্ট্রিম করার জন্য <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-এর হয়ে অনুমতি চাইছে"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"অনুমতি দিন"</string> diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml index 340fd6a1e532..d7423ac9ceee 100644 --- a/packages/CompanionDeviceManager/res/values-bs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Prenosite aplikacije s telefona"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Dozvolite da aplikacija <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pristupa ovim informacijama s telefona"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluga na više uređaja"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> zahtijeva odobrenje da pristupi fotografijama, medijima i odobrenjima na vašem telefonu"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> zahtijeva odobrenje da prenosi aplikacije između vaših uređaja"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Dozvolite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa ovim informacijama s vašeg telefona"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play usluge"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> zahtijeva odobrenje da prenosi aplikacije između vaših uređaja"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> u ime uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> zahtijeva odobrenje da pristupi fotografijama, medijima i odobrenjima na vašem telefonu"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Dozvoli"</string> diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml index 967b390c36a5..a40efd019477 100644 --- a/packages/CompanionDeviceManager/res/values-ca/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Reprodueix en continu aplicacions del telèfon"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permet que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> accedeixi a aquesta informació del telèfon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serveis multidispositiu"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) per accedir a les fotos, el contingut multimèdia i les notificacions del telèfon"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Permet que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> accedeixi a aquesta informació del telèfon"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotos i contingut multimèdia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Serveis de Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> demana permís en nom del teu dispositiu (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) per reproduir en continu aplicacions entre els dispositius"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Permet"</string> diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml index 7ab5f6243b11..cfea6c388351 100644 --- a/packages/CompanionDeviceManager/res/values-cs/strings.xml +++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Streamujte aplikace v telefonu"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Povolte aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> přístup k těmto informacím z vašeho telefonu"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pro více zařízení"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> oprávnění k přístupu k fotkám, médiím a oznámením v telefonu"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Povolte aplikaci <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> přístup k těmto informacím z vašeho telefonu"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotky a média"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Služby Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> požaduje za vaše zařízení <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> oprávnění ke streamování aplikací mezi zařízeními"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Povolit"</string> diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml index 2fb2e6e4d81c..5ba30ec39c51 100644 --- a/packages/CompanionDeviceManager/res/values-da/strings.xml +++ b/packages/CompanionDeviceManager/res/values-da/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Stream din telefons apps"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Giv <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> adgang til disse oplysninger fra din telefon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjenester, som kan tilsluttes en anden enhed"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til at få adgang til din telefons billeder, medier og notifikationer"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til at streame apps mellem dine enheder"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Tillad, at <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> får adgang til disse oplysninger fra din telefon"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Billeder og medier"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play-tjenester"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til at streame apps mellem dine enheder"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> anmoder om tilladelse på vegne af din <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> til at få adgang til din telefons billeder, medier og notifikationer"</string> <string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Tillad"</string> diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml index 0b6a195763c6..21f0322ce873 100644 --- a/packages/CompanionDeviceManager/res/values-de/strings.xml +++ b/packages/CompanionDeviceManager/res/values-de/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Smartphone-Apps streamen"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> Zugriff auf diese Informationen von deinem Smartphone gewähren"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Geräteübergreifende Dienste"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet im Namen deines <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> um die Berechtigung zum Zugriff auf die Fotos, Medien und Benachrichtigungen deines Smartphones"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> Zugriff auf diese Informationen von deinem Smartphone gewähren"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotos und Medien"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play-Dienste"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> bittet im Namen deines <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> um die Berechtigung zum Streamen von Apps zwischen deinen Geräten"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Zulassen"</string> diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml index 726009fb0638..2c27f9281e4c 100644 --- a/packages/CompanionDeviceManager/res/values-el/strings.xml +++ b/packages/CompanionDeviceManager/res/values-el/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Μεταδώστε σε ροή τις εφαρμογές του τηλεφώνου σας"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Να επιτρέπεται στο <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> η πρόσβαση σε αυτές τις πληροφορίες από το τηλέφωνό σας."</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Υπηρεσίες πολλών συσκευών"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> άδεια για πρόσβαση στις φωτογραφίες, τα αρχεία μέσων και τις ειδοποιήσεις του τηλεφώνου σας"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> άδεια για ροή εφαρμογών μεταξύ των συσκευών σας"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Επιτρέψτε στην εφαρμογή <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> να έχει πρόσβαση σε αυτές τις πληροφορίες από το τηλέφωνό σας"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Φωτογραφίες και μέσα"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Υπηρεσίες Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> άδεια για ροή εφαρμογών μεταξύ των συσκευών σας"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> ζητά εκ μέρους της συσκευής σας <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> άδεια για πρόσβαση στις φωτογραφίες, τα αρχεία μέσων και τις ειδοποιήσεις του τηλεφώνου σας"</string> <string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Να επιτρέπεται"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml index dacfaaeee4a3..982101456424 100644 --- a/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rAU/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Stream your phone’s apps"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml index dacfaaeee4a3..982101456424 100644 --- a/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rCA/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Stream your phone’s apps"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml index dacfaaeee4a3..982101456424 100644 --- a/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rGB/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Stream your phone’s apps"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml index dacfaaeee4a3..982101456424 100644 --- a/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rIN/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Stream your phone’s apps"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media and notifications"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> diff --git a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml index 56d979a7b950..ea1ff66c672d 100644 --- a/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml +++ b/packages/CompanionDeviceManager/res/values-en-rXC/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Stream your phone’s apps"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media, and notifications"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> to access this information from your phone"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Photos and media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to stream apps between your devices"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> to access your phone’s photos, media, and notifications"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Allow"</string> diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml index 54d13c4a42a9..143e1cb32178 100644 --- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Transmitir las apps de tu teléfono"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permite que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información de tu teléfono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicios multidispositivo"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acceder a las fotos, el contenido multimedia y las notificaciones de tu teléfono"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Permite que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información de tu teléfono"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotos y contenido multimedia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Servicios de Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicita tu permiso en nombre de <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para transmitir apps entre dispositivos"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml index 0d0b10e7ce4a..6edc02ae647e 100644 --- a/packages/CompanionDeviceManager/res/values-es/strings.xml +++ b/packages/CompanionDeviceManager/res/values-es/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Emite las aplicaciones de tu teléfono"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información desde tu teléfono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicios multidispositivo"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acceder a las fotos, los archivos multimedia y las notificaciones de tu teléfono"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información desde tu teléfono"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotos y elementos multimedia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Servicios de Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> está pidiendo permiso en nombre de tu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para emitir aplicaciones en otros dispositivos tuyos"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml index b1603903cfc7..d15b975bac89 100644 --- a/packages/CompanionDeviceManager/res/values-et/strings.xml +++ b/packages/CompanionDeviceManager/res/values-et/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Telefoni rakenduste voogesitamine"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Lubage rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pääseda teie telefonis juurde sellele teabele"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Seadmeülesed teenused"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> taotlevad teie seadme <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nimel luba pääseda juurde telefoni fotodele, meediale ja märguannetele"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Lubage rakendusel <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pääseda teie telefonis juurde sellele teabele"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotod ja meedia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play teenused"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> taotleb teie seadme <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nimel luba teie seadmete vahel rakendusi voogesitada"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"seade"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Luba"</string> diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml index 395c385948da..fda2c4e3c4b6 100644 --- a/packages/CompanionDeviceManager/res/values-eu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Igorri zuzenean telefonoko aplikazioak"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Eman informazioa telefonotik hartzeko baimena <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Gailu baterako baino gehiagotarako zerbitzuak"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Telefonoko argazkiak, multimedia-edukia eta jakinarazpenak atzitzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuaren izenean"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Eman informazio hori telefonotik hartzeko baimena <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aplikazioari"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Argazkiak eta multimedia-edukia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Gailu batetik bestera aplikazioak igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuaren izenean"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Eman baimena"</string> diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml index beabaf1842bd..0edab4d03d5f 100644 --- a/packages/CompanionDeviceManager/res/values-fa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"جاریسازی برنامههای تلفن"</string> <string name="title_app_streaming" msgid="2270331024626446950">"اجازه دادن به <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> برای دسترسی به اطلاعات تلفن"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"سرویسهای بیندستگاهی"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> اجازه میخواهد ازطرف <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> به عکسها، رسانه، و اعلانهای تلفن شما دسترسی پیدا کند"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> مجاز میشود به این اطلاعات در دستگاهتان دسترسی پیدا کند"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"عکسها و رسانهها"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"خدمات Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> اجازه میخواهد ازطرف <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> برنامهها را بین دستگاههای شما جاریسازی کند"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"اجازه دادن"</string> diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml index 35e0e47a0649..b34bb364df0c 100644 --- a/packages/CompanionDeviceManager/res/values-fi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Striimaa puhelimen sovelluksia"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Salli, että <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> saa pääsyn näihin puhelimesi tietoihin"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Laitteidenväliset palvelut"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteeltasi (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) lupaa päästä puhelimesi kuviin, mediaan ja ilmoituksiin"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Salli pääsy tähän tietoon puhelimellasi: <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Kuvat ja media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play Palvelut"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> pyytää laitteeltasi (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) lupaa striimata sovelluksia laitteidesi välillä"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"laite"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Salli"</string> diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml index 1b1727e8bc10..7e46c468d0b2 100644 --- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Diffusez les applications de votre téléphone"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Autorisez <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à ces informations à partir de votre téléphone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Services multiappareils"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pour accéder aux photos, aux fichiers multimédias et aux notifications de votre téléphone"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Autorisez <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à ces informations à partir de votre téléphone"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Photos et fichiers multimédias"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Services Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pour diffuser des applications entre vos appareils"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string> diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml index 30db3187857e..b8cd4995bf5a 100644 --- a/packages/CompanionDeviceManager/res/values-fr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Diffuser en streaming les applis de votre téléphone"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à ces informations depuis votre téléphone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Services inter-appareils"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pour accéder aux photos, contenus multimédias et notifications de votre téléphone"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Autoriser <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> à accéder à ces informations depuis votre téléphone"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Photos et contenus multimédias"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Services Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> demande l\'autorisation au nom de votre <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> pour caster des applis d\'un appareil à l\'autre"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Autoriser"</string> diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml index c692c034cf22..ae9111ebbcba 100644 --- a/packages/CompanionDeviceManager/res/values-gl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Emite as aplicacións do teu teléfono"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que a aplicación <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información desde o teu teléfono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servizos multidispositivo"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) para acceder ás fotos, ao contido multimedia e ás notificacións do teléfono"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acceda a esta información desde o teu teléfono"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotos e contido multimedia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Servizos de Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> está solicitando permiso en nome do teu dispositivo (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>) para emitir aplicacións entre os teus aparellos"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml index 8c92de8b96e0..747b331dd30d 100644 --- a/packages/CompanionDeviceManager/res/values-gu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"તમારા ફોનની ઍપ સ્ટ્રીમ કરો"</string> <string name="title_app_streaming" msgid="2270331024626446950">"તમારા ફોનમાંથી આ માહિતી ઍક્સેસ કરવા માટે, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને મંજૂરી આપો"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ક્રોસ-ડિવાઇસ સેવાઓ"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> વતી તમારા ફોનના ફોટા, મીડિયા અને નોટિફિકેશન ઍક્સેસ કરવાની પરવાનગીની વિનંતી કરી રહી છે"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"તમારા ફોનમાંથી આ માહિતી ઍક્સેસ કરવા માટે, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>ને મંજૂરી આપો"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"ફોટા અને મીડિયા"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play સેવાઓ"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> તમારા <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> વતી તમારા ડિવાઇસ વચ્ચે ઍપ સ્ટ્રીમ કરવાની પરવાનગીની વિનંતી કરી રહી છે"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"મંજૂરી આપો"</string> diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml index 1ac399946cbe..8f06cc21ae49 100644 --- a/packages/CompanionDeviceManager/res/values-hi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"अपने फ़ोन के ऐप्लिकेशन को स्ट्रीम करें"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को अपने फ़ोन से यह जानकारी ऐक्सेस करने की अनुमति दें"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रॉस-डिवाइस से जुड़ी सेवाएं"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> की ओर से, फ़ोन में मौजूद फ़ोटो, मीडिया, और सूचनाओं को ऐक्सेस करने की अनुमति मांग रही हैं"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> को अपने फ़ोन से यह जानकारी ऐक्सेस करने की अनुमति दें"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"फ़ोटो और मीडिया"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play सेवाएं"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> आपके <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> की ओर से, डिवाइसों के बीच ऐप्लिकेशन को स्ट्रीम करने की अनुमति मांग रहा है"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"अनुमति दें"</string> diff --git a/packages/CompanionDeviceManager/res/values-hr/strings.xml b/packages/CompanionDeviceManager/res/values-hr/strings.xml index 6e186281e3b6..7f8a5893ab8e 100644 --- a/packages/CompanionDeviceManager/res/values-hr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hr/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Streaming aplikacija vašeg telefona"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Omogućite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa informacijama s vašeg telefona"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usluge na različitim uređajima"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> da pristupi fotografijama, medijskim sadržajima i obavijestima na telefonu"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za emitiranje aplikacija između vaših uređaja"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Omogućite aplikaciji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> da pristupa informacijama s vašeg telefona"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotografije i mediji"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Usluge za Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za streamanje aplikacija između vaših uređaja"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> zahtijeva dopuštenje u ime vašeg uređaja <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> za pristup fotografijama, medijskim sadržajima i obavijestima na telefonu"</string> <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Dopusti"</string> diff --git a/packages/CompanionDeviceManager/res/values-hu/strings.xml b/packages/CompanionDeviceManager/res/values-hu/strings.xml index 2f8dd8536844..389a8213dcbd 100644 --- a/packages/CompanionDeviceManager/res/values-hu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hu/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"A telefon alkalmazásainak streamelése"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Engedélyezi a(z) „<xliff:g id="APP_NAME">%1$s</xliff:g>” alkalmazás számára az információhoz való hozzáférést a telefonról"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Többeszközös szolgáltatások"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nevében a telefonon tárolt fotókhoz, médiatartalmakhoz és értesítésekhez való hozzáféréshez"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Engedélyezi a(z) „<xliff:g id="APP_NAME">%1$s</xliff:g>” alkalmazás számára az információhoz való hozzáférést a telefonról"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotók és médiatartalmak"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play-szolgáltatások"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> engedélyt kér a(z) <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nevében az alkalmazások eszközök közötti streameléséhez"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"eszköz"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Engedélyezés"</string> diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml index aed650b7c472..96a6fe28d74f 100644 --- a/packages/CompanionDeviceManager/res/values-hy/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Հեռարձակել հեռախոսի հավելվածները"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Թույլատրեք <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին օգտագործել այս տեղեկությունները ձեր հեռախոսից"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Միջսարքային ծառայություններ"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր հեռախոսի լուսանկարները, մեդիաֆայլերն ու ծանուցումները տեսնելու համար"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Թույլատրեք <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> հավելվածին օգտագործել այս տեղեկությունները ձեր հեռախոսից"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Լուսանկարներ և մուլտիմեդիա"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play ծառայություններ"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը ձեր <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> սարքի անունից թույլտվություն է խնդրում՝ ձեր սարքերի միջև հավելվածներ հեռարձակելու համար"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Թույլատրել"</string> diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml index 048325c4f676..a5fa8011cc87 100644 --- a/packages/CompanionDeviceManager/res/values-in/strings.xml +++ b/packages/CompanionDeviceManager/res/values-in/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Streaming aplikasi ponsel"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengakses informasi ini dari ponsel Anda"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Layanan lintas perangkat"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> untuk mengakses foto, media, dan notifikasi ponsel Anda"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Izinkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mengakses informasi ini dari ponsel Anda"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Foto dan media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Layanan Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> meminta izin atas nama <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> untuk menstreaming aplikasi di antara perangkat Anda"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Izinkan"</string> diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml index 3f5a3de9de77..7313cda259c0 100644 --- a/packages/CompanionDeviceManager/res/values-is/strings.xml +++ b/packages/CompanionDeviceManager/res/values-is/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Streymdu forritum símans"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aðgang að þessum upplýsingum úr símanum þínum"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Þjónustur á milli tækja"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um aðgang að myndum, margmiðlunarefni og tilkynningum símans þíns fyrir hönd <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Veita <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aðgang að þessum upplýsingum úr símanum þínum"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Myndir og efni"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Þjónusta Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um heimild fyrir straumspilun forrita á milli tækjanna þinna fyrir hönd <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Leyfa"</string> diff --git a/packages/CompanionDeviceManager/res/values-it/strings.xml b/packages/CompanionDeviceManager/res/values-it/strings.xml index 0f7fb08c9da3..5ea7c0a43246 100644 --- a/packages/CompanionDeviceManager/res/values-it/strings.xml +++ b/packages/CompanionDeviceManager/res/values-it/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Trasmetti in streaming le app del tuo telefono"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Consenti a <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di accedere a queste informazioni dal tuo telefono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servizi cross-device"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto del tuo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> l\'autorizzazione ad accedere a foto, contenuti multimediali e notifiche del telefono"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Consenti a <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> di accedere a questa informazione dal tuo telefono"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Foto e contenuti multimediali"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> richiede per conto del tuo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> l\'autorizzazione a trasmettere app in streaming tra i dispositivi"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Consenti"</string> diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml index 9622ce54aa6a..d488e2865063 100644 --- a/packages/CompanionDeviceManager/res/values-iw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"שידור אפליקציות מהטלפון"</string> <string name="title_app_streaming" msgid="2270331024626446950">"מתן אישור לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לגשת למידע הזה מהטלפון שלך"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"שירותים למספר מכשירים"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור ה‑<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> כדי לגשת לתמונות, למדיה ולהתראות בטלפון שלך"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור מכשיר <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> כדי לשדר אפליקציות בין המכשירים שלך"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"מתן אישור לאפליקציה <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> לגשת למידע הזה מהטלפון שלך"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"תמונות ומדיה"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור ה‑<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> כדי לשדר אפליקציות בין המכשירים שלך"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> מבקשת הרשאה עבור מכשיר <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> כדי לגשת לתמונות, למדיה ולהתראות בטלפון שלך"</string> <string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"יש אישור"</string> diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml index 97bc56d629c0..041a5a4e1968 100644 --- a/packages/CompanionDeviceManager/res/values-ja/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml @@ -16,7 +16,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="app_label" msgid="4470785958457506021">"コンパニオン デバイス マネージャ"</string> + <string name="app_label" msgid="4470785958457506021">"コンパニオン デバイス マネージャー"</string> <string name="confirmation_title" msgid="3785000297483688997">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> に <strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong> へのアクセスを許可"</string> <string name="profile_name_watch" msgid="576290739483672360">"ウォッチ"</string> <string name="chooser_title" msgid="2262294130493605839">"<strong><xliff:g id="APP_NAME">%2$s</xliff:g></strong> の管理対象となる<xliff:g id="PROFILE_NAME">%1$s</xliff:g>の選択"</string> @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"スマートフォンのアプリのストリーミング"</string> <string name="title_app_streaming" msgid="2270331024626446950">"スマートフォンのこの情報へのアクセスを <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> に許可"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"クロスデバイス サービス"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> に代わってスマートフォンの写真、メディア、通知にアクセスする権限をリクエストしています"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"このスマートフォンからの情報へのアクセスを <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> に許可"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"写真とメディア"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play 開発者サービス"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> に代わってデバイス間でアプリをストリーミングする権限をリクエストしています"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"許可"</string> diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml index 94c33e5df268..6fa113fcb21e 100644 --- a/packages/CompanionDeviceManager/res/values-ka/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"თქვენი ტელეფონის აპების სტრიმინგი"</string> <string name="title_app_streaming" msgid="2270331024626446950">"ნება დართეთ, რომ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> აპს ჰქონდეს ამ ინფორმაციაზე წვდომა თქვენი ტელეფონიდან"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"მოწყობილობათშორისი სერვისები"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ითხოვს უფლებას თქვენი <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-ის სახელით, რომ წვდომა ჰქონდეს თქვენი ტელეფონის ფოტოებზე, მედიასა და შეტყობინებებზე"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"ნება დართეთ, რომ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> აპს ჰქონდეს ამ ინფორმაციაზე წვდომა თქვენი ტელეფონიდან"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"ფოტოები და მედია"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ითხოვს უფლებას თქვენი <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-ის სახელით, რომ მოწყობილობებს შორის სტრიმინგი შეძლოს"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"დაშვება"</string> diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml index 5a4314bf9979..0625210ed3cc 100644 --- a/packages/CompanionDeviceManager/res/values-kk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Телефон қолданбаларын трансляциялайды."</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына телефоныңыздағы осы ақпаратты пайдалануға рұқсат беріңіз."</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Аралық құрылғы қызметтері"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> атынан телефондағы фотосуреттерді, медиафайлдар мен хабарландыруларды пайдалану үшін рұқсат сұрайды."</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> қолданбасына телефоныңыздағы осы ақпаратты пайдалануға рұқсат беріңіз."</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Фотосуреттер мен медиафайлдар"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play қызметтері"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> атынан құрылғылар арасында қолданбалар трансляциялау үшін рұқсат сұрайды."</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Рұқсат беру"</string> diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml index 2456f63f1b34..854fcac990ec 100644 --- a/packages/CompanionDeviceManager/res/values-km/strings.xml +++ b/packages/CompanionDeviceManager/res/values-km/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"ផ្សាយកម្មវិធីរបស់ទូរសព្ទអ្នក"</string> <string name="title_app_streaming" msgid="2270331024626446950">"អនុញ្ញាតឱ្យ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ចូលប្រើព័ត៌មាននេះពីទូរសព្ទរបស់អ្នក"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"សេវាកម្មឆ្លងកាត់ឧបករណ៍"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> របស់អ្នក ដើម្បីចូលប្រើរូបថត មេឌៀ និងការជូនដំណឹងរបស់ទូរសព្ទអ្នក"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"អនុញ្ញាតឱ្យ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ចូលមើលព័ត៌មាននេះពីទូរសព្ទរបស់អ្នក"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"រូបថត និងមេឌៀ"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"សេវាកម្ម Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុងស្នើសុំការអនុញ្ញាតជំនួសឱ្យ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> របស់អ្នក ដើម្បីបញ្ចាំងកម្មវិធីរវាងឧបករណ៍របស់អ្នក"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"អនុញ្ញាត"</string> diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml index 5284ebf93c6c..809a811e1ba4 100644 --- a/packages/CompanionDeviceManager/res/values-kn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"ನಿಮ್ಮ ಫೋನ್ನ ಆ್ಯಪ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಿ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"ನಿಮ್ಮ ಫೋನ್ ಮೂಲಕ ಈ ಮಾಹಿತಿಯನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಗೆ ಅನುಮತಿಸಿ"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ಕ್ರಾಸ್-ಡಿವೈಸ್ ಸೇವೆಗಳು"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"ನಿಮ್ಮ ಫೋನ್ನ ಫೋಟೋಗಳು, ಮೀಡಿಯಾ ಮತ್ತು ಅಧಿಸೂಚನೆಗಳನ್ನು ಪ್ರವೇಶಿಸಲು ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನ ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಿಕೊಳ್ಳುತ್ತಿದೆ"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"ನಿಮ್ಮ ಫೋನ್ ಮೂಲಕ ಈ ಮಾಹಿತಿಯನ್ನು ಪ್ರವೇಶಿಸಲು <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ಗೆ ಅನುಮತಿಸಿ"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"ಫೋಟೋಗಳು ಮತ್ತು ಮಾಧ್ಯಮ"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play ಸೇವೆಗಳು"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"ನಿಮ್ಮ ಸಾಧನಗಳ ನಡುವೆ ಆ್ಯಪ್ಗಳನ್ನು ಸ್ಟ್ರೀಮ್ ಮಾಡಲು ನಿಮ್ಮ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ನ ಪರವಾಗಿ <xliff:g id="APP_NAME">%1$s</xliff:g> ಅನುಮತಿಯನ್ನು ವಿನಂತಿಸಿಕೊಳ್ಳುತ್ತಿದೆ"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"ಅನುಮತಿಸಿ"</string> diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml index 4451cb93456c..79cd1b6f6330 100644 --- a/packages/CompanionDeviceManager/res/values-ko/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"휴대전화의 앱을 스트리밍합니다."</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 앱이 휴대전화에서 이 정보에 액세스하도록 허용합니다."</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"교차 기기 서비스"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 대신 휴대전화의 사진, 미디어, 알림에 액세스할 수 있는 권한을 요청하고 있습니다."</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 앱이 휴대전화에서 이 정보에 액세스하도록 허용합니다."</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"사진 및 미디어"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play 서비스"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 대신 기기 간에 앱을 스트리밍할 수 있는 권한을 요청하고 있습니다."</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"기기"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"허용"</string> diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml index d641f299d652..634804392f42 100644 --- a/packages/CompanionDeviceManager/res/values-ky/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Телефондогу колдонмолорду алып ойнотуу"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Түзмөктөр аралык кызматтар"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан телефондогу сүрөттөрдү, медиа файлдарды жана билдирмелерди колдонууга уруксат сурап жатат"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Сүрөттөр жана медиа"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play кызматтары"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду тышкы экранга чыгарууга уруксат сурап жатат"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Ооба"</string> diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml index f9d65faee9a2..47fbadd1dba7 100644 --- a/packages/CompanionDeviceManager/res/values-lo/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"ສະຕຣີມແອັບຂອງໂທລະສັບທ່ານ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"ອະນຸຍາດ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ໃຫ້ເຂົ້າເຖິງຂໍ້ມູນນີ້ຈາກໂທລະສັບຂອງທ່ານໄດ້"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ບໍລິການຂ້າມອຸປະກອນ"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງຮ້ອງຂໍການອະນຸຍາດໃນນາມຂອງ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ເພື່ອເຂົ້າເຖິງຮູບພາບ, ມີເດຍ ແລະ ການແຈ້ງເຕືອນຂອງໂທລະສັບທ່ານ"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"ອະນຸຍາດ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ໃຫ້ເຂົ້າເຖິງຂໍ້ມູນນີ້ຈາກໂທລະສັບຂອງທ່ານໄດ້"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"ຮູບພາບ ແລະ ມີເດຍ"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"ບໍລິການ Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ກຳລັງຮ້ອງຂໍການອະນຸຍາດໃນນາມຂອງ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ເພື່ອສະຕຣີມແອັບລະຫວ່າງອຸປະກອນຂອງທ່ານ"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"ອະນຸຍາດ"</string> diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml index 6e7b00786221..cc377793a3e3 100644 --- a/packages/CompanionDeviceManager/res/values-lt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Telefono programų perdavimas srautu"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Leisti <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pasiekti šią informaciją iš jūsų telefono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Pasl. keliuose įrenginiuose"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>“ vardu, kad galėtų pasiekti telefono nuotraukas, mediją ir pranešimus"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Leisti <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> pasiekti šią informaciją iš jūsų telefono"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Nuotraukos ir medija"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"„Google Play“ paslaugos"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prašo leidimo jūsų „<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>“ vardu, kad galėtų srautu perduoti programas iš vieno įrenginio į kitą"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Leisti"</string> diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml index 1fdc99d82be8..f1c6f8dbbc24 100644 --- a/packages/CompanionDeviceManager/res/values-lv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Var straumēt jūsu tālruņa lietotnes"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Atļaut lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> piekļūt šai informācijai no jūsu tālruņa"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Vairāku ierīču pakalpojumi"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju piekļūt jūsu tālruņa fotoattēliem, multivides saturam un paziņojumiem šīs ierīces vārdā: <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Atļaut lietotnei <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> piekļūt šai informācijai no jūsu tālruņa"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotoattēli un multivides faili"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play pakalpojumi"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> pieprasa atļauju straumēt lietotnes starp jūsu ierīcēm šīs ierīces vārdā: <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Atļaut"</string> diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml index e09a5b395efa..46fcc02e0c8c 100644 --- a/packages/CompanionDeviceManager/res/values-mk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Стримувајте ги апликациите на телефонот"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Овозможете <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да пристапува до овие податоци на телефонот"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Повеќенаменски услуги"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за да пристапува до фотографиите, аудиовизуелните содржини и известувањата на телефонот"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Овозможете <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> да пристапува до овие податоци на телефонот"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Аудиовизуелни содржини"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Услуги на Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> бара дозвола во име на вашиот <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за да стримува апликации помеѓу вашите уреди"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"уред"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string> diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml index 636a7507940e..27682d4c1af1 100644 --- a/packages/CompanionDeviceManager/res/values-ml/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"നിങ്ങളുടെ ഫോണിലെ ആപ്പുകൾ സ്ട്രീം ചെയ്യാൻ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"നിങ്ങളുടെ ഫോണിൽ നിന്ന് ഈ വിവരങ്ങൾ ആക്സസ് ചെയ്യാൻ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ആപ്പിനെ അനുവദിക്കുക"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ക്രോസ്-ഉപകരണ സേവനങ്ങൾ"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"നിങ്ങളുടെ ഫോണിലെ ഫോട്ടോകൾ, മീഡിയ, അറിയിപ്പുകൾ എന്നിവ ആക്സസ് ചെയ്യാൻ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ഉപകരണത്തിനു വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു."</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"നിങ്ങളുടെ ഫോണിൽ നിന്ന് ഈ വിവരങ്ങൾ ആക്സസ് ചെയ്യാൻ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ആപ്പിനെ അനുവദിക്കുക"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"ഫോട്ടോകളും മീഡിയയും"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play സേവനങ്ങൾ"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"നിങ്ങളുടെ ഉപകരണങ്ങളിൽ ഒന്നിൽ നിന്ന് അടുത്തതിലേക്ക് ആപ്പുകൾ സ്ട്രീം ചെയ്യാൻ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ഉപകരണത്തിനു വേണ്ടി <xliff:g id="APP_NAME">%1$s</xliff:g> അനുമതി അഭ്യർത്ഥിക്കുന്നു."</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"അനുവദിക്കുക"</string> diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml index 64b284dcb6cb..84a986fba631 100644 --- a/packages/CompanionDeviceManager/res/values-mn/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Таны утасны аппуудыг дамжуулах"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-д таны утаснаас энэ мэдээлэлд хандахыг зөвшөөрнө үү"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Төхөөрөмж хоорондын үйлчилгээ"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н өмнөөс утасны зураг, медиа болон мэдэгдэлд хандахын тулд зөвшөөрөл хүсэж байна"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"Таны төхөөрөмжүүд хооронд апп дамжуулахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н өмнөөс зөвшөөрөл хүсэж байна"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>-д таны утаснаас энэ мэдээлэлд хандахыг зөвшөөрнө үү"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Зураг болон медиа"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play үйлчилгээ"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> нь таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н өмнөөс төхөөрөмж хооронд апп дамжуулахын тулд зөвшөөрөл хүсэж байна"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"Таны утасны зураг, медиа болон мэдэгдэлд хандахын тулд <xliff:g id="APP_NAME">%1$s</xliff:g> таны <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>-н өмнөөс зөвшөөрөл хүсэж байна"</string> <string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Зөвшөөрөх"</string> diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml index a070e6199469..24e92e56e316 100644 --- a/packages/CompanionDeviceManager/res/values-mr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"फोनवरील ॲप्स स्ट्रीम करा"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला ही माहिती तुमच्या फोनवरून अॅक्सेस करण्यासाठी अनुमती द्या"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रॉस-डिव्हाइस सेवा"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"तुमच्या फोनमधील फोटो, मीडिया आणि सूचना ॲक्सेस करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ला ही माहिती तुमच्या फोनवरून अॅक्सेस करण्यासाठी अनुमती द्या"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"फोटो आणि मीडिया"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play सेवा"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"तुमच्या डिव्हाइसदरम्यान ॲप्स स्ट्रीम करण्यासाठी <xliff:g id="APP_NAME">%1$s</xliff:g> हे तुमच्या <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> च्या वतीने परवानगीची विनंती करत आहे"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"अनुमती द्या"</string> diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml index e6932e13beaa..037d4de3f253 100644 --- a/packages/CompanionDeviceManager/res/values-ms/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Strim apl telefon anda"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Benarkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> mengakses maklumat ini daripada telefon anda"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Perkhidmatan silang peranti"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang meminta kebenaran bagi pihak <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda untuk mengakses foto, media dan pemberitahuan telefon anda"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Benarkan <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> untuk mengakses maklumat ini daripada telefon anda"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Foto dan media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Perkhidmatan Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang meminta kebenaran bagi pihak <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> anda untuk menstrim apl antara peranti anda"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Benarkan"</string> diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml index 778f7be59870..1362ddfeee82 100644 --- a/packages/CompanionDeviceManager/res/values-my/strings.xml +++ b/packages/CompanionDeviceManager/res/values-my/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"သင့်ဖုန်းရှိအက်ပ်များကို တိုက်ရိုက်လွှင့်နိုင်သည်"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ကို သင့်ဖုန်းမှ ဤအချက်အလက် သုံးခွင့်ပြုမည်"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"စက်များကြားသုံး ဝန်ဆောင်မှုများ"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့်ဖုန်း၏ ဓာတ်ပုံ၊ မီဒီယာနှင့် အကြောင်းကြားချက်များသုံးရန် သင်၏ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင်၏စက်များအကြား အက်ပ်များတိုက်ရိုက်လွှင့်ရန် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> အား သင့်ဖုန်းမှ ဤအချက်အလက် သုံးခွင့်ပြုခြင်း"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"ဓာတ်ပုံနှင့် မီဒီယာများ"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play ဝန်ဆောင်မှုများ"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင်၏စက်များအကြား အက်ပ်များတိုက်ရိုက်လွှင့်ရန် သင်၏ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် သင့်ဖုန်း၏ ဓာတ်ပုံ၊ မီဒီယာနှင့် အကြောင်းကြားချက်များသုံးရန် <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ကိုယ်စား ခွင့်ပြုချက်တောင်းနေသည်"</string> <string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"ခွင့်ပြုရန်"</string> diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml index 274b1a9b5e8c..1b269254e638 100644 --- a/packages/CompanionDeviceManager/res/values-nb/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Strøm appene på telefonen"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Gi <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tilgang til denne informasjonen fra telefonen din"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjenester på flere enheter"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse på vegne av <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> for å få tilgang til bilder, medier og varsler på telefonen din"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Gi <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> tilgang til denne informasjonen fra telefonen din"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Bilder og medier"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play-tjenester"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ber om tillatelse på vegne av <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> for å strømme apper mellom enhetene dine"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Tillat"</string> diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml index ac3338e2c211..95028eb14d79 100644 --- a/packages/CompanionDeviceManager/res/values-ne/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"आफ्नो फोनका एपहरू प्रयोग गर्नुहोस्"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> लाई तपाईंको फोनमा भएको यो जानकारी हेर्ने तथा प्रयोग गर्ने अनुमति दिनुहोस्"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"क्रस-डिभाइस सेवाहरू"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> को तर्फबाट तपाईंको फोनमा भएका फोटो, मिडिया र सूचनाहरू हेर्ने तथा प्रयोग गर्ने अनुमति माग्दै छ"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> लाई तपाईंको फोनमा भएको यो जानकारी हेर्ने तथा प्रयोग गर्ने अनुमति दिनुहोस्"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"फोटो र मिडिया"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> तपाईंको डिभाइस <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> को तर्फबाट तपाईंका कुनै एउटा डिभाइसबाट अर्को डिभाइसमा एप स्ट्रिम गर्ने अनुमति माग्दै छ"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"अनुमति दिनुहोस्"</string> diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml index 3eb49c556a29..31b590357d1b 100644 --- a/packages/CompanionDeviceManager/res/values-nl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"De apps van je telefoon streamen"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang geven tot deze informatie op je telefoon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device-services"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toegang tot de foto\'s, media en meldingen van je telefoon"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toestemming om apps te streamen tussen je apparaten"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> toegang geven tot deze informatie op je telefoon"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Foto\'s en media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play-services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toestemming om apps te streamen tussen je apparaten"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toegang tot de foto\'s, media en meldingen van je telefoon"</string> <string name="profile_name_generic" msgid="6851028682723034988">"apparaat"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Toestaan"</string> diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml index b9c543436e7e..1d63ec381fc4 100644 --- a/packages/CompanionDeviceManager/res/values-or/strings.xml +++ b/packages/CompanionDeviceManager/res/values-or/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"ଆପଣଙ୍କ ଫୋନର ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରନ୍ତୁ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"ଆପଣଙ୍କ ଫୋନରୁ ଏହି ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"କ୍ରସ-ଡିଭାଇସ ସେବାଗୁଡ଼ିକ"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"ଆପଣଙ୍କ ଫୋନର ଫଟୋ, ମିଡିଆ ଏବଂ ବିଜ୍ଞପ୍ତିଗୁଡ଼ିକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"ଆପଣଙ୍କ ଫୋନରୁ ଏହି ସୂଚନାକୁ ଆକ୍ସେସ କରିବା ପାଇଁ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>କୁ ଅନୁମତି ଦିଅନ୍ତୁ"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"ଫଟୋ ଏବଂ ମିଡିଆ"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play ସେବାଗୁଡ଼ିକ"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"ଆପଣଙ୍କ ଡିଭାଇସଗୁଡ଼ିକ ମଧ୍ୟରେ ଆପ୍ସକୁ ଷ୍ଟ୍ରିମ କରିବା ପାଇଁ <xliff:g id="APP_NAME">%1$s</xliff:g> ଆପଣଙ୍କ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ତରଫରୁ ଅନୁମତି ପାଇଁ ଅନୁରୋଧ କରୁଛି"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"ଅନୁମତି ଦିଅନ୍ତୁ"</string> diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml index 3d35e55ed68c..07989f715a84 100644 --- a/packages/CompanionDeviceManager/res/values-pa/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"ਆਪਣੇ ਫ਼ੋਨ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰੋ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੋਂ ਇਸ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"ਕ੍ਰਾਸ-ਡੀਵਾਈਸ ਸੇਵਾਵਾਂ"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦੀਆਂ ਫ਼ੋਟੋਆਂ, ਮੀਡੀਆ ਅਤੇ ਸੂਚਨਾਵਾਂ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੋਂ ਇਸ ਜਾਣਕਾਰੀ ਤੱਕ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿਓ"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"ਫ਼ੋਟੋਆਂ ਅਤੇ ਮੀਡੀਆ"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play ਸੇਵਾਵਾਂ"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਤੁਹਾਡੇ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> ਦੀ ਤਰਫ਼ੋਂ ਤੁਹਾਡੇ ਡੀਵਾਈਸਾਂ ਵਿਚਕਾਰ ਐਪਾਂ ਨੂੰ ਸਟ੍ਰੀਮ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਮੰਗ ਰਹੀ ਹੈ"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"ਆਗਿਆ ਦਿਓ"</string> diff --git a/packages/CompanionDeviceManager/res/values-pl/strings.xml b/packages/CompanionDeviceManager/res/values-pl/strings.xml index 08f6880afa9c..3ccbed7b3175 100644 --- a/packages/CompanionDeviceManager/res/values-pl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pl/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Odtwarzaj strumieniowo aplikacje z telefonu"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Zezwól aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na dostęp do tych informacji na Twoim telefonie"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Usługi na innym urządzeniu"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> o uprawnienia dotyczące dostępu do zdjęć, multimediów i powiadomień na telefonie"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Zezwól aplikacji <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na dostęp do tych informacji na Twoim telefonie"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Zdjęcia i multimedia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Usługi Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> prosi w imieniu urządzenia <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> o uprawnienia dotyczące strumieniowego odtwarzania aplikacji między urządzeniami"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"urządzenie"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Zezwól"</string> diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml index ce83bff93d0d..5c426048c824 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Faça streaming dos apps do seu smartphone"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse estas informações do smartphone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone."</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Autorizar que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse estas informações do smartphone"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotos e mídia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone."</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml index 8d84eb76dded..1ed65bd88d11 100644 --- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Faça stream das apps do telemóvel"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permita que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aceda a estas informações do seu telemóvel"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para aceder às fotos, conteúdo multimédia e notificações do seu telemóvel"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer stream de apps entre os seus dispositivos"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Permita que a app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> aceda a estas informações do seu telemóvel"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotos e multimédia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Serviços do Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer stream de apps entre os seus dispositivos"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> está a pedir autorização em nome do seu dispositivo <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para aceder às fotos, ao conteúdo multimédia e às notificações do seu telemóvel"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml index ce83bff93d0d..5c426048c824 100644 --- a/packages/CompanionDeviceManager/res/values-pt/strings.xml +++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Faça streaming dos apps do seu smartphone"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permitir que o app <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse estas informações do smartphone"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Serviços entre dispositivos"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone."</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Autorizar que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> acesse estas informações do smartphone"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotos e mídia"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play Services"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para fazer streaming de apps entre seus dispositivos"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> está pedindo permissão em nome do seu <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para acessar fotos, mídia e notificações do smartphone."</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Permitir"</string> diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml index b6f8ff7a89b7..276ebfdda3c7 100644 --- a/packages/CompanionDeviceManager/res/values-ro/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Să redea în stream aplicațiile telefonului"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Permiteți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicii pe mai multe dispozitive"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a accesa fotografiile, conținutul media și notificările de pe telefon"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a reda în stream aplicații între dispozitivele dvs."</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Permiteți ca <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> să acceseze aceste informații de pe telefon"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotografii și media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Servicii Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a reda în stream aplicații între dispozitivele dvs."</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a accesa fotografiile, conținutul media și notificările de pe telefon"</string> <string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string> diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml index 492e345b93fd..d5031bd2d5f3 100644 --- a/packages/CompanionDeviceManager/res/values-ru/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Трансляция приложений с телефона."</string> <string name="title_app_streaming" msgid="2270331024626446950">"Разрешите приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> получать эту информацию с вашего телефона"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сервисы стриминга приложений"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает разрешение от имени вашего устройства <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, чтобы получить доступ к фотографиям, медиаконтенту и уведомлениям на телефоне."</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Разрешите приложению <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> получать эту информацию с вашего телефона"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Фотографии и медиафайлы"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Сервисы Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запрашивает доступ от имени вашего устройства <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>, чтобы транслировать приложения между вашими устройствами."</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Разрешить"</string> diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml index 79c36513a575..b2451e9ef41b 100644 --- a/packages/CompanionDeviceManager/res/values-si/strings.xml +++ b/packages/CompanionDeviceManager/res/values-si/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"ඔබගේ දුරකථනයේ යෙදුම් ප්රවාහ කරන්න"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> හට ඔබගේ දුරකථනයෙන් මෙම තොරතුරුවලට ප්රවේශ වීමට ඉඩ දෙන්න"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"හරස්-උපාංග සේවා"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබගේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> වෙනුවෙන් ඔබගේ දුරකථනයෙහි ඡායාරූප, මාධ්ය සහ දැනුම්දීම් වෙත ප්රවේශ වීමට අවසරය ඉල්ලමින් සිටී"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබගේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> වෙනුවෙන් ඔබගේ උපාංග අතර යෙදුම් ප්රවාහ කිරීමට අවසරය ඉල්ලමින් සිටියි"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> හට ඔබගේ දුරකථනයෙන් මෙම තොරතුරුවලට ප්රවේශ වීමට ඉඩ දෙන්න"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"ඡායාරූප සහ මාධ්ය"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play සේවා"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබගේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> වෙනුවෙන් ඔබගේ උපාංග අතර යෙදුම් ප්රවාහ කිරීමට අවසරය ඉල්ලමින් සිටී"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> ඔබගේ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> වෙනුවෙන් ඔබගේ දුරකථනයෙහි ඡායාරූප, මාධ්ය සහ දැනුම්දීම් වෙත ප්රවේශ වීමට අවසරය ඉල්ලමින් සිටියි"</string> <string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"ඉඩ දෙන්න"</string> diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml index 54b6ce6d66ad..787c1854d3e8 100644 --- a/packages/CompanionDeviceManager/res/values-sk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Streamovanie aplikácií v telefóne"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Povoľte aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k týmto informáciám z vášho telefónu"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pre viacero zariadení"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na prístup k fotkám, médiám a upozorneniam vášho telefónu v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na streamovanie aplikácií medzi vašimi zariadeniami v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Povoľte aplikácii <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> prístup k týmto informáciám z vášho telefónu"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotky a médiá"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Služby Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na streamovanie aplikácií medzi vašimi zariadeniami v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na prístup k fotkám, médiám a upozorneniam vášho telefónu v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string> <string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Povoliť"</string> diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml index 883bd0bcdb95..42453bab1d22 100644 --- a/packages/CompanionDeviceManager/res/values-sl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Pretočno predvajanje aplikacij telefona"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Dovolite, da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dostopa do teh podatkov v vašem telefonu"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Storitve za zunanje naprave"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>« zahteva dovoljenje za dostop do fotografij, predstavnosti in obvestil v telefonu."</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Dovolite, da <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> dostopa do teh podatkov v vašem telefonu"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotografije in predstavnost"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Storitve Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> v imenu naprave »<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>« zahteva dovoljenje za pretočno predvajanje aplikacij v vaših napravah."</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Dovoli"</string> diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml index 3e6933ccb8db..2550f71fd665 100644 --- a/packages/CompanionDeviceManager/res/values-sq/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Transmeto aplikacionet e telefonit tënd"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Lejo që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të ketë qasje në këtë informacion nga telefoni yt"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Shërbimet mes pajisjeve"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" po kërkon leje në emër të <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> për të marrë qasje te fotografitë, media dhe njoftimet e telefonit tënd"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Lejo që <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> të ketë qasje në këtë informacion nga telefoni yt"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotografitë dhe media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Shërbimet e Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> po kërkon leje në emër të <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> për të transmetuar aplikacione ndërmjet pajisjeve të tua"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Lejo"</string> diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml index f9a1e0248aaa..354ff2c2ac13 100644 --- a/packages/CompanionDeviceManager/res/values-sr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Стримујте апликације на телефону"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Дозволите да <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> приступа овим информацијама са телефона"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Услуге на више уређаја"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за приступ сликама, медијском садржају и обавештењима са телефона"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за стримовање апликација између уређаја"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Дозволите да <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> приступа овим информацијама са телефона"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Слике и медији"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play услуге"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за стримовање апликација између уређаја"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> захтева дозволу у име уређаја <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> за приступ сликама, медијском садржају и обавештењима са телефона"</string> <string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Дозволи"</string> diff --git a/packages/CompanionDeviceManager/res/values-sv/strings.xml b/packages/CompanionDeviceManager/res/values-sv/strings.xml index b4df61613e9c..99df831a8942 100644 --- a/packages/CompanionDeviceManager/res/values-sv/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sv/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Streama telefonens appar"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Ge <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> åtkomstbehörighet till denna information på telefonen"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Tjänster för flera enheter"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet att ge <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> åtkomst till foton, mediefiler och aviseringar på telefonen"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Ge <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> åtkomstbehörighet till denna information på telefonen"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Foton och media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play-tjänster"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> begär behörighet att låta <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> streama appar mellan enheter"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Tillåt"</string> diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml index 6b5ca21a69de..2618de765c2f 100644 --- a/packages/CompanionDeviceManager/res/values-sw/strings.xml +++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Tiririsha programu za simu yako"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Ruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifikie maelezo haya kutoka kwenye simu yako"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Huduma za kifaa kilichounganishwa kwingine"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili kufikia picha, maudhui na arifa za simu yako"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Ruhusu <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifikie maelezo haya kutoka kwenye simu yako"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Picha na maudhui"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Huduma za Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili kutiririsha programu kati ya vifaa vyako"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string> diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml index 4408e65d8e81..8a32deea6a6b 100644 --- a/packages/CompanionDeviceManager/res/values-ta/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"உங்கள் மொபைலின் ஆப்ஸை ஸ்ட்ரீம் செய்யலாம்"</string> <string name="title_app_streaming" msgid="2270331024626446950">"மொபைலில் உள்ள இந்தத் தகவல்களை அணுக, <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதிக்கவும்"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"பன்முக சாதன சேவைகள்"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"உங்கள் மொபைலில் உள்ள படங்கள், மீடியா, அறிவிப்புகள் ஆகியவற்றை அணுக உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கோருகிறது"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"உங்கள் மொபைலிலிருந்து இந்தத் தகவலை அணுக <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ஆப்ஸை அனுமதியுங்கள்"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"படங்கள் மற்றும் மீடியா"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play சேவைகள்"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"உங்கள் சாதனங்களுக்கு இடையே ஆப்ஸை ஸ்ட்ரீம் செய்ய உங்கள் <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> சார்பாக <xliff:g id="APP_NAME">%1$s</xliff:g> அனுமதி கோருகிறது"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"அனுமதி"</string> diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml index 7bb383fc5f9c..446d91b9b020 100644 --- a/packages/CompanionDeviceManager/res/values-te/strings.xml +++ b/packages/CompanionDeviceManager/res/values-te/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"మీ ఫోన్ యాప్లను స్ట్రీమ్ చేయండి"</string> <string name="title_app_streaming" msgid="2270331024626446950">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> యాప్ను అనుమతించండి"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device services"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> మీ ఫోన్ ఫోటోలు, మీడియా, నోటిఫికేషన్లను యాక్సెస్ చేయడానికి మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> తరపున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> యాప్ను అనుమతించండి"</string> @@ -35,10 +36,11 @@ <string name="permission_storage" msgid="6831099350839392343">"ఫోటోలు, మీడియా"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play సర్వీసులు"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"మీ పరికరాల మధ్య యాప్లను స్ట్రీమ్ చేయడానికి <xliff:g id="APP_NAME">%1$s</xliff:g> మీ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> తరపున అనుమతిని రిక్వెస్ట్ చేస్తోంది"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string> <string name="summary_generic" msgid="2346762210105903720"></string> - <string name="consent_yes" msgid="8344487259618762872">"అనుమతించు"</string> + <string name="consent_yes" msgid="8344487259618762872">"అనుమతించండి"</string> <string name="consent_no" msgid="2640796915611404382">"అనుమతించవద్దు"</string> <string name="consent_back" msgid="2560683030046918882">"వెనుకకు"</string> <string name="permission_sync_confirmation_title" msgid="667074294393493186">"మీ వాచ్కు యాప్ అనుమతులను బదిలీ చేయండి"</string> diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml index e3174f8c9012..a75888269d53 100644 --- a/packages/CompanionDeviceManager/res/values-th/strings.xml +++ b/packages/CompanionDeviceManager/res/values-th/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"สตรีมแอปของโทรศัพท์คุณ"</string> <string name="title_app_streaming" msgid="2270331024626446950">"อนุญาตให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> เข้าถึงข้อมูลนี้จากโทรศัพท์ของคุณ"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"บริการหลายอุปกรณ์"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> เพื่อเข้าถึงรูปภาพ สื่อ และการแจ้งเตือนในโทรศัพท์ของคุณ"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"อนุญาตให้ <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> เข้าถึงข้อมูลนี้จากโทรศัพท์ของคุณ"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"รูปภาพและสื่อ"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"บริการ Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> กำลังขอสิทธิ์ในนามของ <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> เพื่อสตรีมแอประหว่างอุปกรณ์ต่างๆ ของคุณ"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"อนุญาต"</string> diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml index 9c49c5c78df7..d7e9ab69d9f8 100644 --- a/packages/CompanionDeviceManager/res/values-tl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"I-stream ang mga app ng iyong telepono"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Payagan ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na i-access ang impormasyong ito sa iyong telepono"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Mga cross-device na serbisyo"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para i-access ang mga larawan, media, at notification ng telepono mo"</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para mag-stream ng mga app sa pagitan ng mga device mo"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Payagan ang <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> na i-access ang impormasyon sa iyong telepono"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Mga larawan at media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Mga serbisyo ng Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para mag-stream ng mga app sa pagitan ng mga device mo"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"Ang <xliff:g id="APP_NAME">%1$s</xliff:g> ay humihiling ng pahintulot sa ngalan ng iyong <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> para i-access ang mga larawan, media, at notification ng telepono mo"</string> <string name="profile_name_generic" msgid="6851028682723034988">"device"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Payagan"</string> diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml index bdad64149415..826d48657e4e 100644 --- a/packages/CompanionDeviceManager/res/values-tr/strings.xml +++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Telefonunuzun uygulamalarını yayınlama"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasının, telefonunuzdaki bu bilgilere erişmesine izin verin"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Cihazlar arası hizmetler"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g>, telefonunuzdaki fotoğraf, medya ve bildirimlere erişmek için <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınız adına izin istiyor"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> uygulamasının, telefonunuzdaki bu bilgilere erişmesine izin verin"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Fotoğraflar ve medya"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play hizmetleri"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g>, cihazlarınız arasında uygulama akışı gerçekleştirmek için <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> cihazınız adına izin istiyor"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"İzin ver"</string> diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml index 760255b4b64a..89cd2c33a609 100644 --- a/packages/CompanionDeviceManager/res/values-uk/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Транслювати додатки телефона"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Надайте додатку <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ до цієї інформації з телефона"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Сервіси для кількох пристроїв"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> запитує дозвіл на доступ до фотографій, медіафайлів і сповіщень вашого телефона"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Надайте пристрою <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> доступ до цієї інформації з телефона"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Фотографії та медіафайли"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Сервіси Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> від імені вашого пристрою <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> запитує дозвіл на трансляцію додатків між вашими пристроями"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Дозволити"</string> diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml index a311bd4936a8..7f183b8894bd 100644 --- a/packages/CompanionDeviceManager/res/values-ur/strings.xml +++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"اپنے فون کی ایپس کی سلسلہ بندی کریں"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کو اپنے فون سے ان معلومات تک رسائی حاصل کرنے کی اجازت دیں"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"کراس ڈیوائس سروسز"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی جانب سے آپ کے فون کی تصاویر، میڈیا اور اطلاعات تک رسائی کی اجازت طلب کر رہی ہے"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"اپنے فون سے اس معلومات تک رسائی حاصل Allow <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> کرنے کی اجازت دیں"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"تصاویر اور میڈیا"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play سروسز"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی جانب سے آپ کے آلات کے درمیان ایپس کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"اجازت دیں"</string> diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml index f1c162afa666..17426067fabf 100644 --- a/packages/CompanionDeviceManager/res/values-uz/strings.xml +++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Telefondagi ilovalarni translatsiya qilish"</string> <string name="title_app_streaming" msgid="2270331024626446950">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasiga telefondagi ushbu maʼlumot uchun ruxsat bering"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Qurilmalararo xizmatlar"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"Telefoningizdagi rasm, media va bildirishnomalarga kirish uchun <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nomidan ruxsat soʻramoqda"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ilovasiga telefondagi ushbu maʼlumot uchun ruxsat bering"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Suratlar va media"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play xizmatlari"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"Qurilamalararo ilovalar strimingi uchun <xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> nomidan ruxsat soʻramoqda"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Ruxsat"</string> diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml index 5c7d600a83e6..43066140eeb8 100644 --- a/packages/CompanionDeviceManager/res/values-vi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml @@ -25,7 +25,7 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Truyền các ứng dụng trên điện thoại của bạn"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Cho phép <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> truy cập vào thông tin này trên điện thoại của bạn"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Dịch vụ trên nhiều thiết bị"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> để truy cập vào ảnh, nội dung nghe nhìn và thông báo trên điện thoại của bạn."</string> + <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> để truyền trực tuyến ứng dụng giữa các thiết bị của bạn"</string> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Cho phép <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> truy cập vào thông tin này trên điện thoại của bạn"</string> @@ -35,7 +35,7 @@ <string name="permission_storage" msgid="6831099350839392343">"Ảnh và nội dung nghe nhìn"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Dịch vụ Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> để truyền trực tuyến ứng dụng giữa các thiết bị của bạn"</string> + <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang yêu cầu quyền thay cho <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> để truy cập vào ảnh, nội dung nghe nhìn và thông báo trên điện thoại của bạn."</string> <string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Cho phép"</string> diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml index 5a1017f72668..2df8b4ff3d43 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"流式传输手机上的应用"</string> <string name="title_app_streaming" msgid="2270331024626446950">"允许“<xliff:g id="APP_NAME">%1$s</xliff:g>”<strong></strong>访问您手机中的这项信息"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"跨设备服务"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>请求访问您手机上的照片、媒体内容和通知"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"允许 <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> 访问您手机中的这项信息"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"照片和媒体内容"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play 服务"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正代表您的<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>请求在您的设备之间流式传输应用内容"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"设备"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"允许"</string> diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml index 4748ece86a5a..7c2541b2a211 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"串流播放手機應用程式內容"</string> <string name="title_app_streaming" msgid="2270331024626446950">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取您手機中的這項資料"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"跨裝置服務"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 要求必要權限,以便存取手機上的相片、媒體和通知"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取您手機中的這項資料"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"相片和媒體"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play 服務"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表 <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> 要求必要權限,以便在裝置之間串流應用程式內容"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"允許"</string> diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml index f41896fd602f..9dd391900a8b 100644 --- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"串流傳輸手機應用程式內容"</string> <string name="title_app_streaming" msgid="2270331024626446950">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取手機中的這項資訊"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"跨裝置服務"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表你的「<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>」要求必要權限,以便存取手機上的相片、媒體和通知"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"允許「<xliff:g id="APP_NAME">%1$s</xliff:g>」<strong></strong>存取你手機中的這項資訊"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"相片和媒體"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Google Play 服務"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在代表你的「<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>」要求必要權限,以便在裝置之間串流傳輸應用程式內容"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"允許"</string> diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml index 87ed7e6e8adf..5fed63928a59 100644 --- a/packages/CompanionDeviceManager/res/values-zu/strings.xml +++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml @@ -25,7 +25,8 @@ <string name="permission_apps_summary" msgid="798718816711515431">"Sakaza ama-app wefoni yakho"</string> <string name="title_app_streaming" msgid="2270331024626446950">"Vumela i-<strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ifinyelele lolu lwazi kusukela efonini yakho"</string> <string name="helper_title_app_streaming" msgid="4151687003439969765">"Amasevisi amadivayisi amaningi"</string> - <string name="helper_summary_app_streaming" msgid="7380294597268573523">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho ukuze ifinyelele izithombe zefoni yakho, imidiya nezaziso"</string> + <!-- no translation found for helper_summary_app_streaming (5977509499890099) --> + <skip /> <string name="title_automotive_projection" msgid="3296005598978412847"></string> <string name="summary_automotive_projection" msgid="8683801274662496164"></string> <string name="title_computer" msgid="4693714143506569253">"Vumela <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> ukufinyelela lolu lwazi kusuka efonini yakho"</string> @@ -35,7 +36,8 @@ <string name="permission_storage" msgid="6831099350839392343">"Izithombe nemidiya"</string> <string name="permission_storage_summary" msgid="3918240895519506417"></string> <string name="helper_title_computer" msgid="4671071173916176037">"Amasevisi we-Google Play"</string> - <string name="helper_summary_computer" msgid="1676407599909474428">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> icela imvume esikhundleni se-<xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yakho ukuze isakaze-bukhoma ama-app phakathi kwamadivayisi akho"</string> + <!-- no translation found for helper_summary_computer (9050724687678157852) --> + <skip /> <string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string> <string name="summary_generic" msgid="2346762210105903720"></string> <string name="consent_yes" msgid="8344487259618762872">"Vumela"</string> diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml index a389bfc42725..cb85ae4fe66b 100644 --- a/packages/CompanionDeviceManager/res/values/strings.xml +++ b/packages/CompanionDeviceManager/res/values/strings.xml @@ -48,7 +48,7 @@ <string name="helper_title_app_streaming">Cross-device services</string> <!-- Description of the helper dialog for APP_STREAMING profile. [CHAR LIMIT=NONE] --> - <string name="helper_summary_app_streaming"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to access your phone\u2019s photos, media, and notifications</string> + <string name="helper_summary_app_streaming"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to stream apps between your devices</string> <!-- ================= DEVICE_PROFILE_AUTOMOTIVE_PROJECTION ================= --> @@ -82,7 +82,7 @@ <string name="helper_title_computer">Google Play services</string> <!-- Description of the helper dialog for COMPUTER profile. [CHAR LIMIT=NONE] --> - <string name="helper_summary_computer"> <xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to stream apps between your devices</string> + <string name="helper_summary_computer"><xliff:g id="app_name" example="GMS">%1$s</xliff:g> is requesting permission on behalf of your <xliff:g id="device_type" example="Chromebook">%2$s</xliff:g> to access your phone\u2019s photos, media, and notifications</string> <!-- ================= null profile ================= --> diff --git a/packages/DynamicSystemInstallationService/res/values-or/strings.xml b/packages/DynamicSystemInstallationService/res/values-or/strings.xml index 05b9016d45b0..b5ec29259f7c 100644 --- a/packages/DynamicSystemInstallationService/res/values-or/strings.xml +++ b/packages/DynamicSystemInstallationService/res/values-or/strings.xml @@ -7,7 +7,7 @@ <string name="notification_install_failed" msgid="4066039210317521404">"ଇନଷ୍ଟଲ୍ କରିବା ବିଫଳ ହୋଇଛି"</string> <string name="notification_image_validation_failed" msgid="2720357826403917016">"ଇମେଜ୍ ବୈଧକରଣ ବିଫଳ ହୋଇଛି। ଇନଷ୍ଟଲେସନ୍ ରଦ୍ଦ କରନ୍ତୁ।"</string> <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"ବର୍ତ୍ତମାନ ଏକ ଡାଇନାମିକ୍ ସିଷ୍ଟମ୍ ଚାଲୁଛି। ମୂଳ Android ସଂସ୍କରଣ ବ୍ୟବହାର କରିବାକୁ ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ।"</string> - <string name="notification_action_cancel" msgid="5929299408545961077">"ବାତିଲ୍ କରନ୍ତୁ"</string> + <string name="notification_action_cancel" msgid="5929299408545961077">"ବାତିଲ କରନ୍ତୁ"</string> <string name="notification_action_discard" msgid="1817481003134947493">"ଖାରଜ କରନ୍ତୁ"</string> <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ"</string> <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ"</string> diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml index fac338bca0a8..fe6edcea8dd8 100644 --- a/packages/PackageInstaller/res/values-eu/strings.xml +++ b/packages/PackageInstaller/res/values-eu/strings.xml @@ -85,8 +85,8 @@ <string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telebista honetan. Hori aldatzeko, joan Ezarpenak atalera."</string> <string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"Segurtasuna bermatzeko, ezin dira instalatu iturburu honetako aplikazio ezezagunak telefono honetan. Hori aldatzeko, joan Ezarpenak atalera."</string> <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Baliteke telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jasatea. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telefonoari agian gertatuko zaizkion kalteen edo datu-galeren erantzulea."</string> - <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string> - <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartu egingo duzu zeu zarela hura erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea."</string> + <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Baliteke tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jasatea. Aplikazio hau instalatzen baduzu, onartu egingo duzu hura erabiltzeagatik tabletari agian gertatuko zaizkion kalteen edo datu-galeren erantzulea zeu izango zarela."</string> + <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Baliteke telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jasatea. Aplikazio hau instalatzen baduzu, onartu egingo duzu hura erabiltzeagatik telebistari agian gertatuko zaizkion kalteen edo datu-galeren erantzulea zeu izango zarela."</string> <string name="anonymous_source_continue" msgid="4375745439457209366">"Egin aurrera"</string> <string name="external_sources_settings" msgid="4046964413071713807">"Ezarpenak"</string> <string name="wear_app_channel" msgid="1960809674709107850">"Wear aplikazioak instalatzea/desinstalatzea"</string> diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml index 4bc5bec46543..75d5d2ddb1ce 100644 --- a/packages/PackageInstaller/res/values-or/strings.xml +++ b/packages/PackageInstaller/res/values-or/strings.xml @@ -20,7 +20,7 @@ <string name="install" msgid="711829760615509273">"ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"</string> <string name="update" msgid="3932142540719227615">"ଅପଡେଟ୍ କରନ୍ତୁ"</string> <string name="done" msgid="6632441120016885253">"ହୋଇଗଲା"</string> - <string name="cancel" msgid="1018267193425558088">"ବାତିଲ୍ କରନ୍ତୁ"</string> + <string name="cancel" msgid="1018267193425558088">"ବାତିଲ କରନ୍ତୁ"</string> <string name="installing" msgid="4921993079741206516">"ଇନଷ୍ଟଲ୍ କରାଯାଉଛି…"</string> <string name="installing_app" msgid="1165095864863849422">"<xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> ଇନଷ୍ଟଲ୍ କରାଯାଉଛି…"</string> <string name="install_done" msgid="5987363587661783896">"ଆପ ଇନଷ୍ଟଲ ହୋଇଗଲା।"</string> diff --git a/packages/PrintSpooler/res/values-or/strings.xml b/packages/PrintSpooler/res/values-or/strings.xml index fa10909b92ed..6f215d3af18b 100644 --- a/packages/PrintSpooler/res/values-or/strings.xml +++ b/packages/PrintSpooler/res/values-or/strings.xml @@ -83,7 +83,7 @@ <string name="cancelling_notification_title_template" msgid="1821759594704703197">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ବାତିଲ୍ କରାଯାଉଛି"</string> <string name="failed_notification_title_template" msgid="2256217208186530973">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> ପ୍ରିଣ୍ଟର୍ ତ୍ରୁଟି"</string> <string name="blocked_notification_title_template" msgid="1175435827331588646">"ପ୍ରିଣ୍ଟର୍ ଦ୍ୱାରା ରୋକାଯାଇଥିବା <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string> - <string name="cancel" msgid="4373674107267141885">"ବାତିଲ୍ କରନ୍ତୁ"</string> + <string name="cancel" msgid="4373674107267141885">"ବାତିଲ କରନ୍ତୁ"</string> <string name="restart" msgid="2472034227037808749">"ରିଷ୍ଟାର୍ଟ କରନ୍ତୁ"</string> <string name="no_connection_to_printer" msgid="2159246915977282728">"ପ୍ରିଣ୍ଟର୍କୁ କୌଣସି ସଂଯୋଗ ନାହିଁ"</string> <string name="reason_unknown" msgid="5507940196503246139">"ଅଜଣା"</string> diff --git a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml index 50f69d1d692a..c629d96bcf4b 100644 --- a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml +++ b/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml @@ -71,6 +71,8 @@ <TextView android:id="@+id/entity_header_second_summary" style="@style/TextAppearance.EntityHeaderSummary" + android:singleLine="false" + android:maxLines="4" android:layout_width="wrap_content" android:layout_height="wrap_content"/> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 7f1e6f80b868..a52e71899599 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -667,7 +667,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"ነባሪ"</string> <string name="turn_screen_on_title" msgid="3266937298097573424">"ማያ ገጽን ያብሩ"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"ማያ ገጹን ማብራት ይፍቀዱ"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"አንድ መተግበሪያ ማያ ገጹን እንዲያበራ ይፍቀዱለት። ከተሰጠ፣ መተግበሪያው ያለእርስዎ ግልጽ ሐሳብ በማንኛውም ጊዜ ማያ ገጹን ሊያበራ ይችላል።"</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"አንድ መተግበሪያ ማያ ገጹን እንዲያበራ ይፍቀዱለት። ከተሰጠ፣ መተግበሪያው ያለእርስዎ ግልፅ ሐሳብ በማንኛውም ጊዜ ማያ ገጹን ሊያበራ ይችላል።"</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g>ን ማሰራጨት ይቁም?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"<xliff:g id="SWITCHAPP">%1$s</xliff:g>ን ካሰራጩ ወይም ውፅዓትን ከቀየሩ የአሁኑ ስርጭትዎ ይቆማል"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> ያሰራጩ"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index d383919a9d73..8f6de31ac409 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -665,7 +665,7 @@ <string name="physical_keyboard_title" msgid="4811935435315835220">"Fyysinen näppäimistö"</string> <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"Valitse näppäimistöasettelu"</string> <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Oletus"</string> - <string name="turn_screen_on_title" msgid="3266937298097573424">"Käynnistä näyttö"</string> + <string name="turn_screen_on_title" msgid="3266937298097573424">"Näytön käynnistys"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Salli näytön käynnistäminen"</string> <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Salli sovelluksen käynnistää näyttö. Jos sovellus saa luvan, se voi käynnistää näytön itsenäisesti milloin tahansa."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Lopetetaanko <xliff:g id="APP_NAME">%1$s</xliff:g>-sovelluksen lähettäminen?"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index e25124c953b1..db496bac51fe 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -148,7 +148,7 @@ <string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"LE_AUDIO ପାଇଁ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="bluetooth_pairing_accept" msgid="2054232610815498004">"ପେୟାର୍"</string> <string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"ପେୟାର୍"</string> - <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ବାତିଲ୍ କରନ୍ତୁ"</string> + <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ବାତିଲ କରନ୍ତୁ"</string> <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"ପେୟାରିଂ ଫଳରେ ସଂଯୁକ୍ତ ଥିବା ବେଳେ ଆପଣଙ୍କ ସମ୍ପର୍କଗୁଡ଼ିକୁ ଏବଂ କଲ୍ର ଇତିବୃତିକୁ ଆକସେସ୍ ମଞ୍ଜୁର ହୁଏ।"</string> <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ସହ ପେୟାର୍ କରିହେଲା ନାହିଁ।"</string> <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"ଏକ ଭୁଲ୍ PIN କିମ୍ବା ପାସକୀ କାରଣରୁ <xliff:g id="DEVICE_NAME">%1$s</xliff:g> ସହ ପେୟାର୍ କରିପାରିଲା ନାହିଁ।"</string> @@ -305,7 +305,7 @@ <string name="select_private_dns_configuration_title" msgid="7887550926056143018">"ବ୍ୟକ୍ତିଗତ DNS"</string> <string name="select_private_dns_configuration_dialog_title" msgid="3731422918335951912">"ବ୍ୟକ୍ତିଗତ DNS ମୋଡ୍ ବାଛନ୍ତୁ"</string> <string name="private_dns_mode_off" msgid="7065962499349997041">"ବନ୍ଦ"</string> - <string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"ସ୍ଵଚାଳିତ"</string> + <string name="private_dns_mode_opportunistic" msgid="1947864819060442354">"ଅଟୋମେଟିକ"</string> <string name="private_dns_mode_provider" msgid="3619040641762557028">"ବ୍ୟକ୍ତିଗତ DNS ପ୍ରଦାତା ହୋଷ୍ଟନାମ"</string> <string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"DNS ପ୍ରଦାନକାରୀଙ୍କ ହୋଷ୍ଟନାମ ଲେଖନ୍ତୁ"</string> <string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"କନେକ୍ଟ କରିହେଲା ନାହିଁ"</string> @@ -524,7 +524,7 @@ <string name="wifi_tether_connected_summary" msgid="5282919920463340158">"{count,plural, =0{0ଟି ଡିଭାଇସ ସଂଯୁକ୍ତ ହୋଇଛି}=1{1ଟି ଡିଭାଇସ ସଂଯୁକ୍ତ ହୋଇଛି}other{#ଟି ଡିଭାଇସ ସଂଯୁକ୍ତ ହୋଇଛି}}"</string> <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"ଅଧିକ ସମୟ।"</string> <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"କମ୍ ସମୟ।"</string> - <string name="cancel" msgid="5665114069455378395">"ବାତିଲ୍"</string> + <string name="cancel" msgid="5665114069455378395">"ବାତିଲ"</string> <string name="okay" msgid="949938843324579502">"ଠିକ୍ ଅଛି"</string> <string name="done" msgid="381184316122520313">"ହୋଇଗଲା"</string> <string name="alarms_and_reminders_label" msgid="6918395649731424294">"ଆଲାରାମ୍ ଏବଂ ରିମାଇଣ୍ଡରଗୁଡ଼ିକ"</string> @@ -624,7 +624,7 @@ <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ଡିଭାଇସ୍ ଡିଫଲ୍ଟ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ଅକ୍ଷମ କରାଯାଇଛି"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string> - <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ଏହି ପରିବର୍ତ୍ତନ ଲାଗୁ କରିବା ପାଇଁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ନିଶ୍ଚିତ ରୂପେ ରିବୁଟ୍ କରାଯିବା ଆବଶ୍ୟକ। ବର୍ତ୍ତମାନ ରିବୁଟ୍ କରନ୍ତୁ କିମ୍ବା ବାତିଲ୍ କରନ୍ତୁ।"</string> + <string name="cached_apps_freezer_reboot_dialog_text" msgid="695330563489230096">"ଏହି ପରିବର୍ତ୍ତନ ଲାଗୁ କରିବା ପାଇଁ ଆପଣଙ୍କ ଡିଭାଇସକୁ ନିଶ୍ଚିତ ରୂପେ ରିବୁଟ୍ କରାଯିବା ଆବଶ୍ୟକ। ବର୍ତ୍ତମାନ ରିବୁଟ୍ କରନ୍ତୁ କିମ୍ବା ବାତିଲ କରନ୍ତୁ।"</string> <string name="media_transfer_wired_usb_device_name" msgid="7699141088423210903">"ତାରଯୁକ୍ତ ହେଡଫୋନ୍"</string> <string name="wifi_hotspot_switch_on_text" msgid="9212273118217786155">"ଚାଲୁ ଅଛି"</string> <string name="wifi_hotspot_switch_off_text" msgid="7245567251496959764">"ବନ୍ଦ ଅଛି"</string> diff --git a/packages/SettingsLib/res/values-te/arrays.xml b/packages/SettingsLib/res/values-te/arrays.xml index a5b9f79325fd..513fb6e87a10 100644 --- a/packages/SettingsLib/res/values-te/arrays.xml +++ b/packages/SettingsLib/res/values-te/arrays.xml @@ -50,8 +50,8 @@ </string-array> <string-array name="hdcp_checking_titles"> <item msgid="2377230797542526134">"ఎప్పటికీ తనిఖీ చేయవద్దు"</item> - <item msgid="3919638466823112484">"DRM కంటెంట్కు మాత్రమే తనిఖీ చేయండి"</item> - <item msgid="9048424957228926377">"ఎల్లప్పుడూ తనిఖీ చేయండి"</item> + <item msgid="3919638466823112484">"DRM కంటెంట్కు మాత్రమే చెక్ చేయండి"</item> + <item msgid="9048424957228926377">"ఎల్లప్పుడూ చెక్ చేయండి"</item> </string-array> <string-array name="hdcp_checking_summaries"> <item msgid="4045840870658484038">"ఎప్పటికీ HDCP తనిఖీని ఉపయోగించవద్దు"</item> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index bec67ec807ef..e9d3fd0e84ca 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -204,7 +204,7 @@ <string name="tts_status_ok" msgid="8583076006537547379">"<xliff:g id="LOCALE">%1$s</xliff:g>కి పూర్తి మద్దతు ఉంది"</string> <string name="tts_status_requires_network" msgid="8327617638884678896">"<xliff:g id="LOCALE">%1$s</xliff:g>కి నెట్వర్క్ కనెక్షన్ అవసరం"</string> <string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g>కు మద్దతు లేదు"</string> - <string name="tts_status_checking" msgid="8026559918948285013">"తనిఖీ చేస్తోంది..."</string> + <string name="tts_status_checking" msgid="8026559918948285013">"చెక్ చేస్తోంది..."</string> <string name="tts_engine_settings_title" msgid="7849477533103566291">"<xliff:g id="TTS_ENGINE_NAME">%s</xliff:g> కోసం సెట్టింగ్లు"</string> <string name="tts_engine_settings_button" msgid="477155276199968948">"ఇంజిన్ సెట్టింగ్లను ప్రారంభించండి"</string> <string name="tts_engine_preference_section_title" msgid="3861562305498624904">"ప్రాధాన్య ఇంజిన్"</string> @@ -336,7 +336,7 @@ <string name="dev_settings_warning_title" msgid="8251234890169074553">"అభివృద్ధి సెట్టింగ్లను అనుమతించాలా?"</string> <string name="dev_settings_warning_message" msgid="37741686486073668">"ఈ సెట్టింగ్లు అభివృద్ధి వినియోగం కోసం మాత్రమే ఉద్దేశించబడినవి. వీటి వలన మీ పరికరం మరియు దీనిలోని యాప్లు విచ్ఛిన్నం కావచ్చు లేదా తప్పుగా ప్రవర్తించవచ్చు."</string> <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"USB ద్వారా యాప్లను వెరిఫై చేయి"</string> - <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"హానికరమైన ప్రవర్తన కోసం ADB/ADT ద్వారా ఇన్స్టాల్ చేయబడిన యాప్లను తనిఖీ చేయి."</string> + <string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"హానికరమైన ప్రవర్తన కోసం ADB/ADT ద్వారా ఇన్స్టాల్ చేయబడిన యాప్లను చెక్ చేయండి."</string> <string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"పేర్లు (MAC అడ్రస్లు మాత్రమే) లేని బ్లూటూత్ పరికరాలు డిస్ప్లే కాబడతాయి"</string> <string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"రిమోట్ పరికరాల్లో ఆమోదించలేని స్థాయిలో అధిక వాల్యూమ్ ఉండటం లేదా వాల్యూమ్ కంట్రోల్ లేకపోవడం వంటి సమస్యలు ఉంటే బ్లూటూత్ సంపూర్ణ వాల్యూమ్ ఫీచర్ను డిజేబుల్ చేస్తుంది."</string> <string name="bluetooth_enable_gabeldorsche_summary" msgid="2054730331770712629">"బ్లూటూత్ Gabeldorsche ఫీచర్ స్ట్యాక్ను ఎనేబుల్ చేస్తుంది."</string> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index f92cc8eda268..4f84c8c817cb 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -246,9 +246,9 @@ <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (accessing Internet through remote device). [CHAR LIMIT=40] --> <string name="bluetooth_profile_pan">Internet access</string> <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PBAP profile. [CHAR LIMIT=40] --> - <string name="bluetooth_profile_pbap">Contact sharing</string> + <string name="bluetooth_profile_pbap">Contacts and call history sharing</string> <!-- Bluetooth settings. The user-visible summary string that is used whenever referring to the PBAP profile (sharing contacts). [CHAR LIMIT=60] --> - <string name="bluetooth_profile_pbap_summary">Use for contact sharing</string> + <string name="bluetooth_profile_pbap_summary">Use for contacts and call history sharing</string> <!-- Bluetooth settings. The user-visible string that is used whenever referring to the PAN profile (sharing this device's Internet connection). [CHAR LIMIT=40] --> <string name="bluetooth_profile_pan_nap">Internet connection sharing</string> <!-- Bluetooth settings. The user-visible string that is used whenever referring to the map profile. --> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java index bf9debf5ccce..bf6975714acd 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java @@ -87,9 +87,11 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { if (DEBUG) { Log.d(TAG, "Bluetooth service connected"); } - mService = (BluetoothLeBroadcast) proxy; - mIsProfileReady = true; - registerServiceCallBack(mExecutor, mBroadcastCallback); + if(!mIsProfileReady) { + mService = (BluetoothLeBroadcast) proxy; + mIsProfileReady = true; + registerServiceCallBack(mExecutor, mBroadcastCallback); + } } @Override @@ -97,8 +99,10 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile { if (DEBUG) { Log.d(TAG, "Bluetooth service disconnected"); } - mIsProfileReady = false; - unregisterServiceCallBack(mBroadcastCallback); + if(mIsProfileReady) { + mIsProfileReady = false; + unregisterServiceCallBack(mBroadcastCallback); + } } }; diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java index 4939e04dad6a..132a631e25cc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatteryStatus.java @@ -135,7 +135,7 @@ public class BatteryStatus { * @return true if the device is charged */ public boolean isCharged() { - return status == BATTERY_STATUS_FULL || level >= 100; + return isCharged(status, level); } /** @@ -177,4 +177,31 @@ public class BatteryStatus { return "BatteryStatus{status=" + status + ",level=" + level + ",plugged=" + plugged + ",health=" + health + ",maxChargingWattage=" + maxChargingWattage + "}"; } + + /** + * Whether or not the device is charged. Note that some devices never return 100% for + * battery level, so this allows either battery level or status to determine if the + * battery is charged. + * + * @param batteryChangedIntent ACTION_BATTERY_CHANGED intent + * @return true if the device is charged + */ + public static boolean isCharged(Intent batteryChangedIntent) { + int status = batteryChangedIntent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); + int level = batteryChangedIntent.getIntExtra(EXTRA_LEVEL, 0); + return isCharged(status, level); + } + + /** + * Whether or not the device is charged. Note that some devices never return 100% for + * battery level, so this allows either battery level or status to determine if the + * battery is charged. + * + * @param status values for "status" field in the ACTION_BATTERY_CHANGED Intent + * @param level values from 0 to 100 + * @return true if the device is charged + */ + public static boolean isCharged(int status, int level) { + return status == BATTERY_STATUS_FULL || level >= 100; + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index d6d73046bed3..281501e6043c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -22,6 +22,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.Context; import android.graphics.drawable.Drawable; +import android.media.AudioManager; import android.media.RoutingSessionInfo; import android.os.Build; import android.text.TextUtils; @@ -83,6 +84,7 @@ public class LocalMediaManager implements BluetoothCallback { private InfoMediaManager mInfoMediaManager; private String mPackageName; private MediaDevice mOnTransferBluetoothDevice; + private AudioManager mAudioManager; @VisibleForTesting List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); @@ -126,6 +128,7 @@ public class LocalMediaManager implements BluetoothCallback { mPackageName = packageName; mLocalBluetoothManager = LocalBluetoothManager.getInstance(context, /* onInitCallback= */ null); + mAudioManager = context.getSystemService(AudioManager.class); mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); if (mLocalBluetoothManager == null) { Log.e(TAG, "Bluetooth is not supported on this device"); @@ -148,6 +151,7 @@ public class LocalMediaManager implements BluetoothCallback { mInfoMediaManager = infoMediaManager; mPackageName = packageName; mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + mAudioManager = context.getSystemService(AudioManager.class); } /** @@ -527,13 +531,16 @@ public class LocalMediaManager implements BluetoothCallback { synchronized (mMediaDevicesLock) { mMediaDevices.clear(); mMediaDevices.addAll(devices); - // Add disconnected bluetooth devices only when phone output device is available. + // Add muting expected bluetooth devices only when phone output device is available. for (MediaDevice device : devices) { final int type = device.getDeviceType(); if (type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE || type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE) { - mMediaDevices.addAll(buildDisconnectedBluetoothDevice()); + MediaDevice mutingExpectedDevice = getMutingExpectedDevice(); + if (mutingExpectedDevice != null) { + mMediaDevices.add(mutingExpectedDevice); + } break; } } @@ -552,6 +559,34 @@ public class LocalMediaManager implements BluetoothCallback { } } + private MediaDevice getMutingExpectedDevice() { + if (mBluetoothAdapter == null + || mAudioManager.getMutingExpectedDevice() == null) { + Log.w(TAG, "BluetoothAdapter is null or muting expected device not exist"); + return null; + } + final List<BluetoothDevice> bluetoothDevices = + mBluetoothAdapter.getMostRecentlyConnectedDevices(); + final CachedBluetoothDeviceManager cachedDeviceManager = + mLocalBluetoothManager.getCachedDeviceManager(); + for (BluetoothDevice device : bluetoothDevices) { + final CachedBluetoothDevice cachedDevice = + cachedDeviceManager.findDevice(device); + if (isBondedMediaDevice(cachedDevice) && isMutingExpectedDevice(cachedDevice)) { + return new BluetoothMediaDevice(mContext, + cachedDevice, + null, null, mPackageName); + } + } + return null; + } + + private boolean isMutingExpectedDevice(CachedBluetoothDevice cachedDevice) { + return mAudioManager.getMutingExpectedDevice() != null + && cachedDevice.getAddress().equals( + mAudioManager.getMutingExpectedDevice().getAddress()); + } + private List<MediaDevice> buildDisconnectedBluetoothDevice() { if (mBluetoothAdapter == null) { Log.w(TAG, "buildDisconnectedBluetoothDevice() BluetoothAdapter is null"); @@ -595,6 +630,13 @@ public class LocalMediaManager implements BluetoothCallback { return new ArrayList<>(mDisconnectedMediaDevices); } + private boolean isBondedMediaDevice(CachedBluetoothDevice cachedDevice) { + return cachedDevice != null + && cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED + && !cachedDevice.isConnected() + && isMediaDevice(cachedDevice); + } + private boolean isMediaDevice(CachedBluetoothDevice device) { for (LocalBluetoothProfile profile : device.getConnectableProfiles()) { if (profile instanceof A2dpProfile || profile instanceof HearingAidProfile || diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index fa87de2b1353..ffd6b522e394 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -110,6 +110,7 @@ android_library { "androidx.arch.core_core-runtime", "androidx.lifecycle_lifecycle-common-java8", "androidx.lifecycle_lifecycle-extensions", + "androidx.lifecycle_lifecycle-runtime-ktx", "androidx.dynamicanimation_dynamicanimation", "androidx-constraintlayout_constraintlayout", "androidx.exifinterface_exifinterface", @@ -218,6 +219,7 @@ android_library { "androidx.arch.core_core-runtime", "androidx.lifecycle_lifecycle-common-java8", "androidx.lifecycle_lifecycle-extensions", + "androidx.lifecycle_lifecycle-runtime-ktx", "androidx.dynamicanimation_dynamicanimation", "androidx-constraintlayout_constraintlayout", "androidx.exifinterface_exifinterface", diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 1c13b1660e31..6edf13addbca 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -618,6 +618,17 @@ android:excludeFromRecents="true" android:visibleToInstantApps="true"/> + <activity + android:name=".media.MediaProjectionAppSelectorActivity" + android:theme="@style/Theme.SystemUI.MediaProjectionAppSelector" + android:finishOnCloseSystemDialogs="true" + android:excludeFromRecents="true" + android:documentLaunchMode="never" + android:relinquishTaskIdentity="true" + android:configChanges= + "screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden" + android:visibleToInstantApps="true"/> + <!-- started from TvNotificationPanel --> <activity android:name=".statusbar.tv.notifications.TvNotificationPanelActivity" diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index f9a9ef65cab6..154a6fca9d09 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -102,7 +102,7 @@ // // If you don't use @Staging or @Postsubmit, your new test will immediately // block presubmit, which is probably not what you want! - "platinum-postsubmit": [ + "sysui-platinum-postsubmit": [ { "name": "PlatformScenarioTests", "options": [ @@ -121,7 +121,7 @@ ] } ], - "staged-platinum-postsubmit": [ + "sysui-staged-platinum-postsubmit": [ { "name": "PlatformScenarioTests", "options": [ diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt index fbe33565f11d..8ddd430dadbc 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt @@ -523,9 +523,10 @@ class ActivityLaunchAnimator( state: LaunchAnimator.State, linearProgress: Float, ) { - if (transactionApplierView.viewRootImpl == null) { - // If the view root we synchronize with was detached, don't apply any transaction - // (as [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw). + if (transactionApplierView.viewRootImpl == null || !window.leash.isValid) { + // Don't apply any transaction if the view root we synchronize with was detached or + // if the SurfaceControl associated with [window] is not valid, as + // [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw. return } @@ -605,9 +606,10 @@ class ActivityLaunchAnimator( state: LaunchAnimator.State, linearProgress: Float ) { - if (transactionApplierView.viewRootImpl == null) { - // If the view root we synchronize with was detached, don't apply any transaction - // (as [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw). + if (transactionApplierView.viewRootImpl == null || !navigationBar.leash.isValid) { + // Don't apply any transaction if the view root we synchronize with was detached or + // if the SurfaceControl associated with [navigationBar] is not valid, as + // [SyncRtSurfaceTransactionApplier.scheduleApply] would otherwise throw. return } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt index cb16d7c3471f..2f36ab9aa93d 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt @@ -150,15 +150,16 @@ constructor( fun showFromDialog( dialog: Dialog, animateFrom: Dialog, + cuj: DialogCuj? = null, animateBackgroundBoundsChange: Boolean = false ) { val view = openedDialogs.firstOrNull { it.dialog == animateFrom }?.dialogContentWithBackground ?: throw IllegalStateException( "The animateFrom dialog was not animated using " + - "DialogLaunchAnimator.showFrom(View|Dialog)" - ) - showFromView(dialog, view, animateBackgroundBoundsChange = animateBackgroundBoundsChange) + "DialogLaunchAnimator.showFrom(View|Dialog)") + showFromView( + dialog, view, animateBackgroundBoundsChange = animateBackgroundBoundsChange, cuj = cuj) } /** @@ -514,11 +515,20 @@ private class AnimatedDialog( dialogContentWithBackground.setTransitionVisibility(View.INVISIBLE) // Make sure the dialog is visible instantly and does not do any window animation. - window.attributes.windowAnimations = R.style.Animation_LaunchAnimation + val attributes = window.attributes + attributes.windowAnimations = R.style.Animation_LaunchAnimation // Ensure that the animation is not clipped by the display cut-out when animating this // dialog into an app. - window.attributes.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS + attributes.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS + + // Ensure that the animation is not clipped by the navigation/task bars when animating this + // dialog into an app. + val wasFittingNavigationBars = + attributes.fitInsetsTypes and WindowInsets.Type.navigationBars() != 0 + attributes.fitInsetsTypes = + attributes.fitInsetsTypes and WindowInsets.Type.navigationBars().inv() + window.attributes = window.attributes // We apply the insets ourselves to make sure that the paddings are set on the correct @@ -526,7 +536,13 @@ private class AnimatedDialog( window.setDecorFitsSystemWindows(false) val viewWithInsets = (dialogContentWithBackground.parent as ViewGroup) viewWithInsets.setOnApplyWindowInsetsListener { view, windowInsets -> - val insets = windowInsets.getInsets(WindowInsets.Type.displayCutout()) + val type = if (wasFittingNavigationBars) { + WindowInsets.Type.displayCutout() or WindowInsets.Type.navigationBars() + } else { + WindowInsets.Type.displayCutout() + } + + val insets = windowInsets.getInsets(type) view.setPadding(insets.left, insets.top, insets.right, insets.bottom) WindowInsets.CONSUMED } diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt index 47f448d503c6..eb000ad312d7 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewLaunchAnimatorController.kt @@ -49,17 +49,16 @@ private const val TAG = "GhostedViewLaunchAnimatorController" * Note: Avoid instantiating this directly and call [ActivityLaunchAnimator.Controller.fromView] * whenever possible instead. */ -open class GhostedViewLaunchAnimatorController( +open class GhostedViewLaunchAnimatorController @JvmOverloads constructor( /** The view that will be ghosted and from which the background will be extracted. */ private val ghostedView: View, /** The [InteractionJankMonitor.CujType] associated to this animation. */ private val cujType: Int? = null, - private var interactionJankMonitor: InteractionJankMonitor? = null + private var interactionJankMonitor: InteractionJankMonitor = + InteractionJankMonitor.getInstance(), ) : ActivityLaunchAnimator.Controller { - constructor(view: View, type: Int) : this(view, type, null) - /** The container to which we will add the ghost view and expanding background. */ override var launchContainer = ghostedView.rootView as ViewGroup private val launchContainerOverlay: ViewGroupOverlay @@ -203,7 +202,7 @@ open class GhostedViewLaunchAnimatorController( val matrix = ghostView?.animationMatrix ?: Matrix.IDENTITY_MATRIX matrix.getValues(initialGhostViewMatrixValues) - cujType?.let { interactionJankMonitor?.begin(ghostedView, it) } + cujType?.let { interactionJankMonitor.begin(ghostedView, it) } } override fun onLaunchAnimationProgress( @@ -289,7 +288,7 @@ open class GhostedViewLaunchAnimatorController( return } - cujType?.let { interactionJankMonitor?.end(it) } + cujType?.let { interactionJankMonitor.end(it) } backgroundDrawable?.wrapped?.alpha = startBackgroundAlpha diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt index 4b0c62bff60d..cc7d23e97d0c 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt @@ -106,7 +106,7 @@ class ViewHierarchyAnimator { ephemeral: Boolean ): Boolean { if ( - !isVisible( + !occupiesSpace( rootView.visibility, rootView.left, rootView.top, @@ -177,7 +177,7 @@ class ViewHierarchyAnimator { fadeInInterpolator: Interpolator = DEFAULT_FADE_IN_INTERPOLATOR ): Boolean { if ( - isVisible( + occupiesSpace( rootView.visibility, rootView.left, rootView.top, @@ -295,7 +295,7 @@ class ViewHierarchyAnimator { (view.getTag(R.id.tag_animator) as? ObjectAnimator)?.cancel() - if (!isVisible(view.visibility, left, top, right, bottom)) { + if (!occupiesSpace(view.visibility, left, top, right, bottom)) { setBound(view, Bound.LEFT, left) setBound(view, Bound.TOP, top) setBound(view, Bound.RIGHT, right) @@ -362,7 +362,7 @@ class ViewHierarchyAnimator { duration: Long = DEFAULT_DURATION ): Boolean { if ( - !isVisible( + !occupiesSpace( rootView.visibility, rootView.left, rootView.top, @@ -530,17 +530,17 @@ class ViewHierarchyAnimator { } /** - * Returns whether the given [visibility] and bounds are consistent with a view being - * currently visible on screen. + * Returns whether the given [visibility] and bounds are consistent with a view being a + * contributing part of the hierarchy. */ - private fun isVisible( + private fun occupiesSpace( visibility: Int, left: Int, top: Int, right: Int, bottom: Int ): Boolean { - return visibility == View.VISIBLE && left != right && top != bottom + return visibility != View.GONE && left != right && top != bottom } /** diff --git a/packages/SystemUI/docs/camera.md b/packages/SystemUI/docs/camera.md index cabc65c53caa..a1c845832049 100644 --- a/packages/SystemUI/docs/camera.md +++ b/packages/SystemUI/docs/camera.md @@ -1,34 +1,23 @@ # How double-click power launches the camera - -_as of august 2020_ - +_Last update: July 2022_ ## Sequence of events - - - -1. [PhoneWindowManager.java](/services/core/java/com/android/server/policy/PhoneWindowManager.java) is responsible for all power button presses (see `interceptPowerKeyDown`). -2. Even though PWMgr has a lot of logic to detect all manner of power button multipresses and gestures, it also checks with GestureLauncherService, which is also [offered the chance](/services/core/java/com/android/server/policy/PhoneWindowManager.java#943) to [intercept](/services/core/java/com/android/server/GestureLauncherService.java#358) the power key. -3. GLS is responsible for the camera timeout, and if it detects one, it [forwards it to the StatusBarManagerService](/services/core/java/com/android/server/GestureLauncherService.java#475) (which hands it off to SystemUI). -4. Inside SystemUI, [onCameraLaunchDetected](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#3927) looks at the keyguard state and determines +1. [PhoneWindowManager.java](/services/core/java/com/android/server/policy/PhoneWindowManager.java) is responsible for all power button presses (see `interceptPowerKeyDown`) +2. Even though `PhoneWindowManager` has a lot of logic to detect all manner of power button multi-presses and gestures, it also checks with `GestureLauncherService`, which is also [offered the chance](/services/core/java/com/android/server/policy/PhoneWindowManager.java#943) to [intercept](/services/core/java/com/android/server/GestureLauncherService.java) the power key +3. `GestureLauncherService` is responsible for the camera timeout, and if it detects one, it [forwards it to the StatusBarManagerService](/services/core/java/com/android/server/GestureLauncherService.java) (which hands it off to SystemUI) +4. Inside SystemUI, `onCameraLaunchDetected` in [CentralSurfacesCommandQueueCallbacks.java](/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java) looks at the keyguard state and determines 1. whether the camera is even allowed 2. whether the screen is on; if not, we need to delay until that happens 3. whether the device is locked (defined as "keyguard is showing"). -5. If the device is unlocked (no keyguard), the camera is launched immediately. [Callsite in onCameraLaunchGestureDetected](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#4047). -6. If the keyguard is up, however, [KeyguardBottomAreaView.launchCamera](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#477) takes over to handle the "secure camera" (a different intent, usually directing to the same app, but giving that app the cue to not allow access to the photo roll, etc). -7. If the intent [would have to launch a resolver](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#480) (the user has multiple cameras installed and hasn’t chosen one to always launch for the `SECURE_CAMERA_INTENT`), - 1. In order to show the resolver, the lockscreen "bouncer" (authentication method) [is first presented](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#523). -8. Otherwise (just one secure camera), [it is launched](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#501) (with some window animation gymnastics). - - -## Which intent launches? - - - -* If the keyguard is not showing (device is unlocked) - * `CameraIntents.getInsecureCameraIntent()`, defined to be `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA`. - * [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java#3950) in StatusBar.java. -* If the keyguard is showing (device locked) - * [KeyguardBottomAreaView.getCameraIntent()](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#366) is consulted, which allows the "keyguard right button" (which we don’t actually show) to control the camera intent. The [default implementation](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#831) returns one of `CameraIntents.getInsecureCameraIntent()` or `CameraIntents.getSecureCameraIntent()`, which are `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA` and `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE`, respectively. - * [Callsite](/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java#523) in KeyguardBottomAreaView.java. -* Note that starting in Android 12, as required by some OEMs, if the special string resource `config_cameraGesturePackage` is nonempty, this will be treated as a package name to be added to the insecure camera intent, constraining the invocation to that single app and typically preventing implicit intent resolution. This package must be on the device or the camera gesture will no longer work properly.
\ No newline at end of file +5. If the device is unlocked (no keyguard), the camera is launched immediately +6. If the keyguard is up, however, [NotificationPanelViewController.launchCamera](/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java) takes over to handle the "secure camera" (a different intent, usually directing to the same app, but giving that app the cue to not allow access to the photo roll, etc). +7. If the intent would have to launch a resolver (because the user has multiple camera apps installed and has not chosen one to always launch for the `SECURE_CAMERA_INTENT`, then - in order to show the resolver, the lockscreen "bouncer" (authentication method) is first presented +8. Otherwise (just one secure camera), it is launched + +## Which intent launches the camera app? +[CameraGestureHelper](/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt) encapsulate this logic. Roughly: +* If the keyguard is not showing (device is unlocked) + * `CameraIntents.getInsecureCameraIntent()`, defined to be `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA`. +* If the keyguard is showing (device is locked) + * one of `CameraIntents.getInsecureCameraIntent()` or `CameraIntents.getSecureCameraIntent()`, which are `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA` and `MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE`, respectively +* Note that starting in Android 12, as required by some OEMs, if the special string resource `config_cameraGesturePackage` is nonempty, this will be treated as a package name to be added to the insecure camera intent, constraining the invocation to that single app and typically preventing implicit intent resolution. This package must be on the device or the camera gesture will no longer work properly diff --git a/packages/SystemUI/docs/user-file-manager.md b/packages/SystemUI/docs/user-file-manager.md new file mode 100644 index 000000000000..52fa2066fbe1 --- /dev/null +++ b/packages/SystemUI/docs/user-file-manager.md @@ -0,0 +1,33 @@ +# UserFileManager + +This class is used to generate file paths and SharedPreferences that is compatible for specific OS +users in SystemUI. Due to constraints in SystemUI, we can only read/write files as the system user. +Therefore, for secondary users, we want to store secondary user specific files into the system user +directory. + + +## Usages + +Inject UserFileManager into your class. + +### fun getFile(fileName: String, userId: Int): File +Add a file name and user id. You can retrieve the current user id from UserTracker. This will +return a java.io File object that contains the file path to write/read to. + +i.e. `fileManager.getFile("example.xml", userTracker.userId)` + +### fun getSharedPreferences(fileName: String, mode: Int, userId: Int): SharedPreferences +Add a file name, user id, and PreferencesMode. You can retrieve the current user id from +UserTracker. This returns SharedPreferences object that is tied to the specific user. Note that if +the SharedPreferences file does not exist, one will be created automatically. See +[SharedPreferences documentation](https://developer.android.com/reference/android/content/Context#getSharedPreferences(java.lang.String,%20int)) +for more details. + +i.e. `fileManager.getSharedPreferences("prefs.xml", userTracker.userId, 0)` + +## Handling User Removal + +This class will listen for Intent.ACTION_USER_REMOVED and remove directories that no longer +corresponding to active users. Additionally, upon start up, the class will run the same query for +deletion to ensure that there is no stale data. + diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java index 3058d9466121..bef61b867f7d 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockPlugin.java @@ -23,7 +23,9 @@ import java.util.TimeZone; /** * Plugin used to replace main clock in keyguard. + * @deprecated Migrating to ClockProviderPlugin */ +@Deprecated @ProvidesInterface(action = ClockPlugin.ACTION, version = ClockPlugin.VERSION) public interface ClockPlugin extends Plugin { diff --git a/packages/SystemUI/proguard.flags b/packages/SystemUI/proguard.flags index e74b6c78ec80..5b9299c278a9 100644 --- a/packages/SystemUI/proguard.flags +++ b/packages/SystemUI/proguard.flags @@ -5,9 +5,9 @@ -keep class com.android.systemui.statusbar.car.CarStatusBar -keep class com.android.systemui.statusbar.phone.CentralSurfaces -keep class com.android.systemui.statusbar.tv.TvStatusBar --keep class com.android.systemui.car.CarSystemUIFactory --keep class com.android.systemui.SystemUIFactory --keep class com.android.systemui.tv.TvSystemUIFactory +-keep class ** extends com.android.systemui.SystemUIInitializer { + *; +} -keep class * extends com.android.systemui.CoreStartable -keep class * implements com.android.systemui.CoreStartable$Injector diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml index 36035fc87e40..01e3de2315af 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_bouncer_user_switcher.xml @@ -45,6 +45,7 @@ style="@style/Bouncer.UserSwitcher.Spinner.Header" android:clickable="false" android:id="@+id/user_switcher_header" + android:textDirection="locale" android:layout_width="@dimen/bouncer_user_switcher_width" android:layout_height="wrap_content" /> </LinearLayout>> diff --git a/packages/SystemUI/res/drawable/dream_overlay_camera_off.xml b/packages/SystemUI/res/drawable/dream_overlay_camera_off.xml new file mode 100644 index 000000000000..159655e39d24 --- /dev/null +++ b/packages/SystemUI/res/drawable/dream_overlay_camera_off.xml @@ -0,0 +1,29 @@ +<?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="56dp" + android:height="24dp" + android:viewportWidth="56" + android:viewportHeight="24"> + <path + android:pathData="M12,0L44,0A12,12 0,0 1,56 12L56,12A12,12 0,0 1,44 24L12,24A12,12 0,0 1,0 12L0,12A12,12 0,0 1,12 0z" + android:fillColor="#5F6368"/> + <path + android:pathData="M21.872,5.873L20.926,6.813L21.492,7.38C21.392,7.566 21.332,7.773 21.332,8V16C21.332,16.733 21.932,17.333 22.666,17.333H30.666C30.892,17.333 31.099,17.273 31.286,17.173L33.186,19.073L34.126,18.133L31.999,16L21.872,5.873ZM31.999,10.986V8C31.999,7.266 31.399,6.666 30.666,6.666H24.552L25.886,8H30.666V12.78L31.999,14.113V13.013L34.666,15.666V8.333L31.999,10.986ZM22.666,8.553V16H30.112L22.666,8.553Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> +</vector> diff --git a/packages/SystemUI/res/drawable/dream_overlay_mic_and_camera_off.xml b/packages/SystemUI/res/drawable/dream_overlay_mic_and_camera_off.xml new file mode 100644 index 000000000000..087dde78833f --- /dev/null +++ b/packages/SystemUI/res/drawable/dream_overlay_mic_and_camera_off.xml @@ -0,0 +1,33 @@ +<?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="56dp" + android:height="24dp" + android:viewportWidth="56" + android:viewportHeight="24"> + <path + android:pathData="M12,0L44,0A12,12 0,0 1,56 12L56,12A12,12 0,0 1,44 24L12,24A12,12 0,0 1,0 12L0,12A12,12 0,0 1,12 0z" + android:fillColor="#5F6368"/> + <path + android:pathData="M15.332,7.333C15.332,6.966 15.632,6.666 15.999,6.666C16.366,6.666 16.666,6.966 16.666,7.333V10.78L17.879,11.993C17.952,11.786 17.999,11.566 17.999,11.333V7.333C17.999,6.226 17.106,5.333 15.999,5.333C14.892,5.333 13.999,6.226 13.999,7.333V8.113L15.332,9.446V7.333ZM9.872,5.873L8.926,6.813L16.692,14.58C16.472,14.633 16.239,14.666 15.999,14.666C14.159,14.666 12.666,13.173 12.666,11.333H11.332C11.332,13.686 13.072,15.62 15.332,15.946V18H16.666V15.946C17.046,15.893 17.412,15.786 17.759,15.64L21.186,19.066L22.126,18.126L9.872,5.873ZM19.332,11.333H20.666C20.666,12.313 20.359,13.213 19.846,13.96L18.872,12.986C19.159,12.5 19.332,11.94 19.332,11.333Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M33.872,5.873L32.926,6.813L33.492,7.38C33.392,7.566 33.332,7.773 33.332,8V16C33.332,16.733 33.932,17.333 34.666,17.333H42.666C42.892,17.333 43.099,17.273 43.286,17.173L45.186,19.073L46.126,18.133L43.999,16L33.872,5.873ZM43.999,10.986V8C43.999,7.266 43.399,6.666 42.666,6.666H36.552L37.886,8H42.666V12.78L43.999,14.113V13.013L46.666,15.666V8.333L43.999,10.986ZM34.666,8.553V16H42.112L34.666,8.553Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> +</vector> diff --git a/packages/SystemUI/res/drawable/dream_overlay_mic_off.xml b/packages/SystemUI/res/drawable/dream_overlay_mic_off.xml new file mode 100644 index 000000000000..693250d39f95 --- /dev/null +++ b/packages/SystemUI/res/drawable/dream_overlay_mic_off.xml @@ -0,0 +1,29 @@ +<?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="56dp" + android:height="24dp" + android:viewportWidth="56" + android:viewportHeight="24"> + <path + android:pathData="M12,0L44,0A12,12 0,0 1,56 12L56,12A12,12 0,0 1,44 24L12,24A12,12 0,0 1,0 12L0,12A12,12 0,0 1,12 0z" + android:fillColor="#5F6368"/> + <path + android:pathData="M27.807,7.133C27.807,6.767 28.107,6.467 28.473,6.467C28.84,6.467 29.14,6.767 29.14,7.133V10.58L30.353,11.793C30.427,11.587 30.473,11.367 30.473,11.133V7.133C30.473,6.027 29.58,5.133 28.473,5.133C27.367,5.133 26.473,6.027 26.473,7.133V7.913L27.807,9.247V7.133ZM22.347,5.673L21.4,6.613L29.167,14.38C28.947,14.433 28.713,14.467 28.473,14.467C26.633,14.467 25.14,12.973 25.14,11.133H23.807C23.807,13.487 25.547,15.42 27.807,15.747V17.8H29.14V15.747C29.52,15.693 29.887,15.587 30.233,15.44L33.66,18.867L34.6,17.927L22.347,5.673ZM31.807,11.133H33.14C33.14,12.113 32.833,13.013 32.32,13.76L31.347,12.787C31.633,12.3 31.807,11.74 31.807,11.133Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> +</vector> diff --git a/packages/SystemUI/res/drawable/ic_media_pause_container.xml b/packages/SystemUI/res/drawable/ic_media_pause_container.xml index b92e63575b95..ea9eb8cd5475 100644 --- a/packages/SystemUI/res/drawable/ic_media_pause_container.xml +++ b/packages/SystemUI/res/drawable/ic_media_pause_container.xml @@ -22,11 +22,6 @@ android:viewportHeight="48"
android:viewportWidth="48">
<group android:name="_R_G">
- <group android:name="_R_G_L_1_G"
- android:translateX="24"
- android:translateY="24"
- android:scaleX="0.5"
- android:scaleY="0.5"/>
<group android:name="_R_G_L_0_G"
android:translateX="24"
android:translateY="24"
@@ -46,7 +41,7 @@ <aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator android:propertyName="pathData"
- android:duration="500"
+ android:duration="250"
android:startOffset="0"
android:valueFrom="M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "
android:valueTo="M48 0.25 C48,0.25 48,0 48,0 C47.75,26 31.25,48 0,48 C0,48 0,48 0,48 C-30,48 -48,25.75 -48,-0.25 C-48,-0.25 -48,-0.25 -48,-0.25 C-47.75,-23.5 -32.25,-47.75 0.5,-48 C0.5,-48 0.5,-48 0.5,-48 C28,-47.75 47.75,-29.75 48,0.25c "
@@ -62,7 +57,7 @@ <aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator android:propertyName="translateX"
- android:duration="517"
+ android:duration="267"
android:startOffset="0"
android:valueFrom="0"
android:valueTo="1"
@@ -70,4 +65,4 @@ </set>
</aapt:attr>
</target>
-</animated-vector>
\ No newline at end of file +</animated-vector>
diff --git a/packages/SystemUI/res/drawable/ic_media_play_container.xml b/packages/SystemUI/res/drawable/ic_media_play_container.xml index 2fc9fc88a4fd..4cb011a89f35 100644 --- a/packages/SystemUI/res/drawable/ic_media_play_container.xml +++ b/packages/SystemUI/res/drawable/ic_media_play_container.xml @@ -41,7 +41,7 @@ <aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator android:propertyName="pathData"
- android:duration="500"
+ android:duration="250"
android:startOffset="0"
android:valueFrom="M48 0.25 C48,0.25 48,0 48,0 C47.75,26 31.25,48 0,48 C0,48 0,48 0,48 C-30,48 -48,25.75 -48,-0.25 C-48,-0.25 -48,-0.25 -48,-0.25 C-47.75,-23.5 -32.25,-47.75 0.5,-48 C0.5,-48 0.5,-48 0.5,-48 C28,-47.75 47.75,-29.75 48,0.25c "
android:valueTo="M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "
@@ -57,7 +57,7 @@ <aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator android:propertyName="translateX"
- android:duration="517"
+ android:duration="267"
android:startOffset="0"
android:valueFrom="0"
android:valueTo="1"
@@ -65,4 +65,4 @@ </set>
</aapt:attr>
</target>
-</animated-vector>
\ No newline at end of file +</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml new file mode 100644 index 000000000000..f239a8dd4291 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml @@ -0,0 +1,53 @@ +<?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. + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="183" + 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="12" + 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.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c M1.5 1.5 C1.5,1.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml new file mode 100644 index 000000000000..d46fafa25816 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml @@ -0,0 +1,98 @@ +<?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. + --> +<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="667" + android:pathData="M 12.125,34.75C 12.104,30.958 12.021,15.792 12,12" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="0"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 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="517" + android:pathData="M 12,12C 12.021,8.312 12.104,-6.436999999999999 12.125,-10.125" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="0"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,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_1_G" + android:translateX="12.125" + android:translateY="34.75"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M10 4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.5 1.5,-8.5 C1.5,-9.33 0.83,-10 0,-10 C-0.83,-10 -1.5,-9.33 -1.5,-8.5 C-1.5,-8.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4c " /> + </group> + <group + android:name="_R_G_L_0_G" + android:translateX="12" + 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.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c M1.5 1.5 C1.5,1.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_battery_saver_icon_off.xml b/packages/SystemUI/res/drawable/qs_battery_saver_icon_off.xml new file mode 100644 index 000000000000..7b9f23d133ea --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_battery_saver_icon_off.xml @@ -0,0 +1,67 @@ +<?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. + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="183" + 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="12" + android:translateY="12"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:pathData=" M3.67 -8 C3.67,-8 2,-8 2,-8 C2,-8 2,-10 2,-10 C2,-10 -2,-10 -2,-10 C-2,-10 -2,-8 -2,-8 C-2,-8 -3.67,-8 -3.67,-8 C-4.4,-8 -5,-7.4 -5,-6.67 C-5,-6.67 -5,8.66 -5,8.66 C-5,9.4 -4.4,10 -3.67,10 C-3.67,10 3.66,10 3.66,10 C4.4,10 5,9.4 5,8.67 C5,8.67 5,-6.67 5,-6.67 C5,-7.4 4.4,-8 3.67,-8c " + android:strokeAlpha="1" + android:strokeColor="#ffffff" + android:strokeLineCap="round" + android:strokeLineJoin="round" + android:strokeWidth="2" /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M2 -10 C2,-10 2,-7 2,-7 C2,-7 -2,-7 -2,-7 C-2,-7 -2,-10 -2,-10 C-2,-10 2,-10 2,-10c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_battery_saver_icon_on.xml b/packages/SystemUI/res/drawable/qs_battery_saver_icon_on.xml new file mode 100644 index 000000000000..5e4af398e017 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_battery_saver_icon_on.xml @@ -0,0 +1,636 @@ +<?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. + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="_R_G_L_4_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="317" + 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="83" + android:propertyName="fillAlpha" + android:startOffset="317" + 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_4_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="417" + android:pathData="M 0,0.875C 0,-0.9690000000000001 0,-8.344000000000001 0,-10.188" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="0"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_4_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="417" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="0" + android:valueTo="112" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c1,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_3_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + 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="83" + android:propertyName="fillAlpha" + android:startOffset="250" + 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="217" + 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="83" + android:propertyName="fillAlpha" + android:startOffset="550" + 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_3_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="83" + android:pathData="M -0.875,9C -0.854,6.156000000000001 -0.896,11.844 -0.875,9" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="0"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="550" + android:pathData="M -0.875,9C -0.854,6.156000000000001 -0.771,-5.218 -0.75,-8.062" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="83"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_3_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="83" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="67" + android:valueTo="67" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="550" + android:propertyName="rotation" + android:startOffset="83" + android:valueFrom="67" + android:valueTo="192" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 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="417" + 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="83" + android:propertyName="fillAlpha" + android:startOffset="417" + 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="267" + android:propertyName="fillAlpha" + android:startOffset="500" + 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="83" + android:propertyName="fillAlpha" + android:startOffset="767" + 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_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:pathData="M 2.125,9.375C 2.146,6.468999999999999 2.104,12.281 2.125,9.375" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="0"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="600" + android:pathData="M 2.125,9.375C 2.146,6.468999999999999 2.229,-5.155999999999999 2.25,-8.062" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="250"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 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="250" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="28" + android:valueTo="28" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="600" + android:propertyName="rotation" + android:startOffset="250" + android:valueFrom="28" + android:valueTo="153" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + </set> + </aapt:attr> + </target> + <target android:name="_R_G_L_1_G_D_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="533" + 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="83" + android:propertyName="fillAlpha" + android:startOffset="533" + 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="250" + android:propertyName="fillAlpha" + android:startOffset="617" + 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="83" + android:propertyName="fillAlpha" + android:startOffset="867" + 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_1_G_T_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="350" + android:pathData="M -2,10C -2,6.99 -2,13.01 -2,10" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="0"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="600" + android:pathData="M -2,10C -2,6.99 -2,-5.052 -2,-8.062" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="350"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 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="350" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="28" + android:valueTo="28" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="600" + android:propertyName="rotation" + android:startOffset="350" + android:valueFrom="28" + android:valueTo="153" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.833,0.833 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="883" + 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="83" + android:propertyName="fillAlpha" + android:startOffset="883" + 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> + </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="667" + android:pathData="M 0,11.5C 0,9.729 0,13.271 0,11.5" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="0"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="667" + android:pathData="M 0,11.5C 0,9.729 0,2.646 0,0.875" + android:propertyName="translateXY" + android:propertyXName="translateX" + android:propertyYName="translateY" + android:startOffset="667"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,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="667" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="-48" + android:valueTo="-48" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="667" + android:propertyName="rotation" + android:startOffset="667" + android:valueFrom="-48" + android:valueTo="0" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.6,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="1350" + 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_5_G" + android:translateX="12" + android:translateY="12"> + <path + android:name="_R_G_L_5_G_D_0_P_0" + android:pathData=" M3.67 -8 C3.67,-8 2,-8 2,-8 C2,-8 2,-10 2,-10 C2,-10 -2,-10 -2,-10 C-2,-10 -2,-8 -2,-8 C-2,-8 -3.67,-8 -3.67,-8 C-4.4,-8 -5,-7.4 -5,-6.67 C-5,-6.67 -5,8.66 -5,8.66 C-5,9.4 -4.4,10 -3.67,10 C-3.67,10 3.66,10 3.66,10 C4.4,10 5,9.4 5,8.67 C5,8.67 5,-6.67 5,-6.67 C5,-7.4 4.4,-8 3.67,-8c " + android:strokeAlpha="1" + android:strokeColor="#ffffff" + android:strokeLineCap="round" + android:strokeLineJoin="round" + android:strokeWidth="2" /> + <path + android:name="_R_G_L_5_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M2 -10 C2,-10 2,-7 2,-7 C2,-7 -2,-7 -2,-7 C-2,-7 -2,-10 -2,-10 C-2,-10 2,-10 2,-10c " /> + </group> + <group + android:name="_R_G_L_4_G_N_6_T_0" + android:translateX="12" + android:translateY="12"> + <group + android:name="_R_G_L_4_G_T_1" + android:rotation="0" + android:translateX="0" + android:translateY="0.875"> + <group + android:name="_R_G_L_4_G" + android:translateY="-0.875"> + <path + android:name="_R_G_L_4_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " /> + </group> + </group> + </group> + <group + android:name="_R_G_L_3_G_N_6_T_0" + android:translateX="12" + android:translateY="12"> + <group + android:name="_R_G_L_3_G_T_1" + android:rotation="67" + android:scaleX="0.7" + android:scaleY="0.7" + android:translateX="-0.875" + android:translateY="9"> + <group + android:name="_R_G_L_3_G" + android:translateY="-0.875"> + <path + android:name="_R_G_L_3_G_D_0_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " /> + </group> + </group> + </group> + <group + android:name="_R_G_L_2_G_N_6_T_0" + android:translateX="12" + android:translateY="12"> + <group + android:name="_R_G_L_2_G_T_1" + android:rotation="28" + android:scaleX="0.85" + android:scaleY="0.85" + android:translateX="2.125" + android:translateY="9.375"> + <group + android:name="_R_G_L_2_G" + android:translateY="-0.875"> + <path + android:name="_R_G_L_2_G_D_0_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " /> + </group> + </group> + </group> + <group + android:name="_R_G_L_1_G_N_6_T_0" + android:translateX="12" + android:translateY="12"> + <group + android:name="_R_G_L_1_G_T_1" + android:rotation="28" + android:scaleX="0.55" + android:scaleY="0.55" + android:translateX="-2" + android:translateY="10"> + <group + android:name="_R_G_L_1_G" + android:translateY="-0.875"> + <path + android:name="_R_G_L_1_G_D_0_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " /> + </group> + </group> + </group> + <group + android:name="_R_G_L_0_G_N_6_T_0" + android:translateX="12" + android:translateY="12"> + <group + android:name="_R_G_L_0_G_T_1" + android:rotation="-48" + android:translateX="0" + android:translateY="11.5"> + <group + android:name="_R_G_L_0_G" + android:translateY="-0.875"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " /> + </group> + </group> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_camera_access_icon_off.xml b/packages/SystemUI/res/drawable/qs_camera_access_icon_off.xml new file mode 100644 index 000000000000..8f9ecbe482e3 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_camera_access_icon_off.xml @@ -0,0 +1,110 @@ +<?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 + --> +<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_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -6.84,-6.68 -6.84,-6.68 C-6.54,-6.37 -6.07,-5.56 -5.49,-5 C-5.49,-5 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.32,3.73 3.32,3.73 C3.32,3.73 5,5.23 5,5.23 C5,5.23 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c M3.34 5 C3.34,5 -6.67,5 -6.67,5 C-6.67,5 -6.67,-5.01 -6.67,-5.01 C-6.67,-5.01 -5.5,-5 -5.5,-5 C-5.98,-5.57 -6.46,-6.23 -6.83,-6.68 C-7.84,-6.64 -8.34,-5.77 -8.34,-5.01 C-8.34,-5.01 -8.34,5 -8.34,5 C-8.34,5.91 -7.58,6.66 -6.67,6.66 C-6.67,6.66 3.34,6.66 3.34,6.66 C4.13,6.66 4.88,6.1 5,5.23 C4.73,4.96 3.73,4.13 3.34,3.73 C3.34,3.73 3.34,5 3.34,5c " + android:valueTo="M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -4.28,-6.67 -4.28,-6.67 C-3.98,-6.36 -3.13,-5.56 -2.55,-4.99 C-2.55,-4.99 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.33,0.9 3.33,0.9 C3.33,0.9 5.01,2.59 5.01,2.59 C5.01,2.59 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c M3.34 5 C3.34,5 -6.67,5 -6.67,5 C-6.67,5 -6.67,-5.01 -6.67,-5.01 C-6.67,-5.01 -5.5,-5 -5.5,-5 C-6.02,-5.5 -6.69,-6.17 -7.12,-6.61 C-7.82,-6.41 -8.34,-5.77 -8.34,-5.01 C-8.34,-5.01 -8.34,5 -8.34,5 C-8.34,5.91 -7.58,6.66 -6.67,6.66 C-6.67,6.66 3.34,6.66 3.34,6.66 C4.13,6.66 4.79,6.11 4.97,5.37 C4.7,5.1 3.73,4.13 3.34,3.73 C3.34,3.73 3.34,5 3.34,5c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,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="17" + 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> + </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="167" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-10.96 -7.79 C-10.96,-7.79 -10.96,-7.79 -10.96,-7.79 C-10.96,-7.79 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 -10.96,-7.79 -10.96,-7.79c " + android:valueTo="M7.44 10.61 C7.44,10.61 -10.96,-7.79 -10.96,-7.79 C-10.96,-7.79 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 8.84,9.21 8.84,9.21 C8.84,9.21 7.44,10.61 7.44,10.61c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,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="183" + 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="12" + 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=" M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -6.84,-6.68 -6.84,-6.68 C-6.54,-6.37 -6.07,-5.56 -5.49,-5 C-5.49,-5 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.32,3.73 3.32,3.73 C3.32,3.73 5,5.23 5,5.23 C5,5.23 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c " /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="0" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-10.96 -7.79 C-10.96,-7.79 -10.96,-7.79 -10.96,-7.79 C-10.96,-7.79 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 -10.96,-7.79 -10.96,-7.79c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_camera_access_icon_on.xml b/packages/SystemUI/res/drawable/qs_camera_access_icon_on.xml new file mode 100644 index 000000000000..beb8b72d29ad --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_camera_access_icon_on.xml @@ -0,0 +1,93 @@ +<?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 + --> +<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_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M7.44 10.61 C7.44,10.61 -10.96,-7.79 -10.96,-7.79 C-10.96,-7.79 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 8.84,9.21 8.84,9.21 C8.84,9.21 7.44,10.61 7.44,10.61c " + android:valueTo="M7.44 10.62 C7.44,10.62 7.45,10.62 7.45,10.62 C7.45,10.62 8.85,9.22 8.85,9.22 C8.85,9.22 8.84,9.22 8.84,9.22 C8.84,9.22 7.44,10.62 7.44,10.62c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.55,0 0.833,0.833 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="333" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -4.28,-6.67 -4.28,-6.67 C-3.98,-6.36 -3.13,-5.56 -2.55,-4.99 C-2.55,-4.99 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.33,0.9 3.33,0.9 C3.33,0.9 5.01,2.59 5.01,2.59 C5.01,2.59 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c M3.34 5 C3.34,5 -6.67,5 -6.67,5 C-6.67,5 -6.67,-5.01 -6.67,-5.01 C-6.67,-5.01 -5.5,-5 -5.5,-5 C-6.02,-5.5 -6.69,-6.17 -7.12,-6.61 C-7.82,-6.41 -8.34,-5.77 -8.34,-5.01 C-8.34,-5.01 -8.34,5 -8.34,5 C-8.34,5.91 -7.58,6.66 -6.67,6.66 C-6.67,6.66 3.34,6.66 3.34,6.66 C4.13,6.66 4.79,6.11 4.97,5.37 C4.7,5.1 3.73,4.13 3.34,3.73 C3.34,3.73 3.34,5 3.34,5c " + android:valueTo="M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -6.84,-6.68 -6.84,-6.68 C-6.54,-6.37 -6.07,-5.56 -5.49,-5 C-5.49,-5 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.32,3.73 3.32,3.73 C3.32,3.73 5,5.23 5,5.23 C5,5.23 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c M3.34 5 C3.34,5 -6.67,5 -6.67,5 C-6.67,5 -6.67,-5.01 -6.67,-5.01 C-6.67,-5.01 -5.5,-5 -5.5,-5 C-5.98,-5.57 -6.46,-6.23 -6.83,-6.68 C-7.84,-6.64 -8.34,-5.77 -8.34,-5.01 C-8.34,-5.01 -8.34,5 -8.34,5 C-8.34,5.91 -7.58,6.66 -6.67,6.66 C-6.67,6.66 3.34,6.66 3.34,6.66 C4.13,6.66 4.88,6.1 5,5.23 C4.73,4.96 3.73,4.13 3.34,3.73 C3.34,3.73 3.34,5 3.34,5c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.1,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="350" + 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="12" + 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=" M7.44 10.61 C7.44,10.61 -10.96,-7.79 -10.96,-7.79 C-10.96,-7.79 -9.56,-9.19 -9.56,-9.19 C-9.56,-9.19 8.84,9.21 8.84,9.21 C8.84,9.21 7.44,10.61 7.44,10.61c " /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M5 1.3 C5,1.3 8.35,4.63 8.35,4.63 C8.35,4.63 8.35,-4.58 8.35,-4.58 C8.35,-4.58 5,-1.25 5,-1.25 C5,-1.25 5,-5 5,-5 C5,-5.92 4.25,-6.67 3.33,-6.67 C3.33,-6.67 -4.28,-6.67 -4.28,-6.67 C-3.98,-6.36 -3.13,-5.56 -2.55,-4.99 C-2.55,-4.99 3.33,-5 3.33,-5 C3.33,-5 3.33,-1.91 3.33,-1.91 C3.33,-1.91 3.33,0.9 3.33,0.9 C3.33,0.9 5.01,2.59 5.01,2.59 C5.01,2.59 5,2.14 5,2.14 C5,2.14 5,1.3 5,1.3c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_dnd_icon_off.xml b/packages/SystemUI/res/drawable/qs_dnd_icon_off.xml new file mode 100644 index 000000000000..e42381a4d5a3 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_dnd_icon_off.xml @@ -0,0 +1,56 @@ +<?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 + --> +<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <target android:name="time_group"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="183" + 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:rotation="-225" + android:scaleX="0.85" + android:scaleY="0.85" + android:translateX="12" + 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-2.83 -4.24 C-2.83,-4.24 4.24,2.83 4.24,2.83 C4.24,2.83 2.83,4.24 2.83,4.24 C2.83,4.24 -4.24,-2.83 -4.24,-2.83 C-4.24,-2.83 -2.83,-4.24 -2.83,-4.24c M7.07 7.09 C4.65,9.51 1.78,10.02 0,10.02 C-5.52,10.02 -10,5.54 -10,0.02 C-10,-1.76 -9.49,-4.52 -7.07,-7.07 C-7.07,-7.07 -5.66,-5.67 -5.66,-5.67 C-7.73,-3.44 -8,-1.2 -8,0.02 C-8,4.43 -4.41,8.02 0,8.02 C1.22,8.02 3.42,7.71 5.67,5.64 C5.67,5.64 7.07,7.09 7.07,7.09c M-7.06 -7.1 C-4.62,-9.54 -1.81,-9.94 -0.03,-9.94 C5.49,-9.94 9.97,-5.46 9.97,0.06 C9.97,1.84 9.49,4.63 7.07,7.05 C7.07,7.05 5.66,5.64 5.66,5.64 C7.67,3.51 7.97,1.28 7.97,0.06 C7.97,-4.35 4.38,-7.94 -0.03,-7.94 C-1.25,-7.94 -3.43,-7.88 -5.65,-5.66 C-5.65,-5.66 -7.06,-7.1 -7.06,-7.1c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_dnd_icon_on.xml b/packages/SystemUI/res/drawable/qs_dnd_icon_on.xml new file mode 100644 index 000000000000..a63cb238f5a0 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_dnd_icon_on.xml @@ -0,0 +1,73 @@ +<?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 + --> +<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"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="500" + android:propertyName="rotation" + android:startOffset="0" + android:valueFrom="-225" + android:valueTo="-45" + android:valueType="floatType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,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="517" + 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:rotation="-225" + android:scaleX="0.85" + android:scaleY="0.85" + android:translateX="12" + 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-2.83 -4.24 C-2.83,-4.24 4.24,2.83 4.24,2.83 C4.24,2.83 2.83,4.24 2.83,4.24 C2.83,4.24 -4.24,-2.83 -4.24,-2.83 C-4.24,-2.83 -2.83,-4.24 -2.83,-4.24c M7.07 7.09 C4.65,9.51 1.78,10.02 0,10.02 C-5.52,10.02 -10,5.54 -10,0.02 C-10,-1.76 -9.49,-4.52 -7.07,-7.07 C-7.07,-7.07 -5.66,-5.67 -5.66,-5.67 C-7.73,-3.44 -8,-1.2 -8,0.02 C-8,4.43 -4.41,8.02 0,8.02 C1.22,8.02 3.42,7.71 5.67,5.64 C5.67,5.64 7.07,7.09 7.07,7.09c M-7.06 -7.1 C-4.62,-9.54 -1.81,-9.94 -0.03,-9.94 C5.49,-9.94 9.97,-5.46 9.97,0.06 C9.97,1.84 9.49,4.63 7.07,7.05 C7.07,7.05 5.66,5.64 5.66,5.64 C7.67,3.51 7.97,1.28 7.97,0.06 C7.97,-4.35 4.38,-7.94 -0.03,-7.94 C-1.25,-7.94 -3.43,-7.88 -5.65,-5.66 C-5.65,-5.66 -7.06,-7.1 -7.06,-7.1c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_location_icon_off.xml b/packages/SystemUI/res/drawable/qs_location_icon_off.xml new file mode 100644 index 000000000000..97eb91cd33b0 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_location_icon_off.xml @@ -0,0 +1,166 @@ +<?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. + --> +<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="175" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M3.98 4.01 C3.98,4.01 2.56,2.54 2.56,2.54 C3.34,1.49 4.41,0.01 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -4.01,-6.61 -4.61,-4.61 C-4.61,-4.61 -6.24,-6.22 -6.24,-6.22 C-5.27,-8.11 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.86,-0.99 6.49,0.02 C6.12,1.04 4.57,3.23 3.98,4.01c " + android:valueTo="M4.62 3.11 C4.62,3.11 3.2,1.65 3.2,1.65 C3.77,0.85 4.43,-0.12 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.55,-6.78 -4.16,-5.65 C-4.16,-5.65 -5.67,-7.13 -5.67,-7.13 C-4.81,-8.35 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.85,-0.89 6.49,0.02 C6.13,0.94 5.16,2.35 4.62,3.11c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="75" + android:propertyName="pathData" + android:startOffset="175" + android:valueFrom="M4.62 3.11 C4.62,3.11 3.2,1.65 3.2,1.65 C3.77,0.85 4.43,-0.12 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.55,-6.78 -4.16,-5.65 C-4.16,-5.65 -5.67,-7.13 -5.67,-7.13 C-4.81,-8.35 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.85,-0.89 6.49,0.02 C6.13,0.94 5.16,2.35 4.62,3.11c " + android:valueTo="M5.18 2.33 C5.18,2.33 3.75,0.88 3.75,0.88 C4.13,0.29 4.45,-0.23 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.16,-6.94 -3.61,-6.46 C-3.61,-6.46 -5.07,-7.86 -5.07,-7.86 C-4.42,-8.54 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.84,-0.81 6.49,0.02 C6.14,0.86 5.68,1.58 5.18,2.33c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 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_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="125" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-0.96 -0.67 C0.37,-0.16 1.79,-0.77 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-2.08,-4.88 -2.84,-3.58 -2.35,-2.11 C-2.16,-1.55 -1.58,-0.91 -0.96,-0.67c " + android:valueTo="M-0.29 -0.52 C0.81,-0.39 1.86,-0.95 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.93,-4.94 -2.6,-3.89 -2.49,-2.82 C-2.18,-2.46 -0.67,-0.84 -0.29,-0.52c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="50" + android:propertyName="pathData" + android:startOffset="125" + android:valueFrom="M-0.29 -0.52 C0.81,-0.39 1.86,-0.95 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.93,-4.94 -2.6,-3.89 -2.49,-2.82 C-2.18,-2.46 -0.67,-0.84 -0.29,-0.52c " + android:valueTo="M0.87 -0.65 C1.56,-0.93 2.02,-1.3 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.63,-5.06 -2.14,-4.44 -2.33,-3.91 C-2.11,-3.68 0.66,-0.85 0.87,-0.65c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="75" + android:propertyName="pathData" + android:startOffset="175" + android:valueFrom="M0.87 -0.65 C1.56,-0.93 2.02,-1.3 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.63,-5.06 -2.14,-4.44 -2.33,-3.91 C-2.11,-3.68 0.66,-0.85 0.87,-0.65c " + android:valueTo="M1.8 -1.24 C2.03,-1.47 2.21,-1.76 2.33,-2.05 C2.44,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.25,-5.21 -1.57,-5.02 -1.77,-4.78 C-1.77,-4.78 1.8,-1.24 1.8,-1.24c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 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="125" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c " + android:valueTo="M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="125" + android:propertyName="pathData" + android:startOffset="125" + android:valueFrom="M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c " + android:valueTo="M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,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_0_G" + android:translateX="12" + 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-5.05 -2.1 C-5.05,-1.06 -4.64,-0.4 -4.23,0.3 C-3.81,1 -3.28,1.75 -2.65,2.55 C-2.22,3.1 -1.78,3.71 -1.35,4.39 C-0.92,5.06 -0.39,5.6 0.01,6.43 C0.21,5.95 0.61,5.27 0.98,4.72 C1.44,4.01 2.13,3.09 2.55,2.54 C2.94,2.95 3.54,3.56 3.98,4 C3.47,4.64 2.65,5.82 2.1,6.77 C1.66,7.55 1.28,8.44 0.98,9.26 C0.88,9.55 0.77,9.67 0.59,9.81 C0.44,9.93 0.28,9.99 0,9.99 C-0.28,9.99 -0.48,9.89 -0.68,9.66 C-0.88,9.44 -1.03,9.18 -1.15,8.9 C-1.45,8.07 -1.68,7.43 -2.17,6.65 C-2.67,5.88 -3.27,4.87 -4.15,3.79 C-4.88,2.75 -5.67,1.6 -6.21,0.62 C-6.74,-0.36 -7,-1.76 -7,-3.01 C-7,-4.08 -6.77,-5.28 -6.23,-6.23 C-5.75,-5.73 -4.92,-4.92 -4.61,-4.61 C-4.87,-3.95 -5.06,-2.93 -5.05,-2.1c " /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M3.98 4.01 C3.98,4.01 2.56,2.54 2.56,2.54 C3.34,1.49 4.41,0.01 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -4.01,-6.61 -4.61,-4.61 C-4.61,-4.61 -6.24,-6.22 -6.24,-6.22 C-5.27,-8.11 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.86,-0.99 6.49,0.02 C6.12,1.04 4.57,3.23 3.98,4.01c " /> + <path + android:name="_R_G_L_0_G_D_1_P_1" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-0.96 -0.67 C0.37,-0.16 1.79,-0.77 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-2.08,-4.88 -2.84,-3.58 -2.35,-2.11 C-2.16,-1.55 -1.58,-0.91 -0.96,-0.67c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="1" + android:fillColor="#edf2eb" + android:fillType="nonZero" + android:pathData=" M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_location_icon_on.xml b/packages/SystemUI/res/drawable/qs_location_icon_on.xml new file mode 100644 index 000000000000..c56b6508eb49 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_location_icon_on.xml @@ -0,0 +1,155 @@ +<?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. + --> +<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="100" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M5.18 2.33 C5.18,2.33 3.75,0.88 3.75,0.88 C4.13,0.29 4.45,-0.23 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.16,-6.94 -3.61,-6.46 C-3.61,-6.46 -5.07,-7.86 -5.07,-7.86 C-4.42,-8.54 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.84,-0.81 6.49,0.02 C6.14,0.86 5.68,1.58 5.18,2.33c " + android:valueTo="M4.62 3.11 C4.62,3.11 3.2,1.65 3.2,1.65 C3.77,0.85 4.43,-0.12 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.55,-6.78 -4.16,-5.65 C-4.16,-5.65 -5.67,-7.13 -5.67,-7.13 C-4.81,-8.35 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.85,-0.89 6.49,0.02 C6.13,0.94 5.16,2.35 4.62,3.11c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="233" + android:propertyName="pathData" + android:startOffset="100" + android:valueFrom="M4.62 3.11 C4.62,3.11 3.2,1.65 3.2,1.65 C3.77,0.85 4.43,-0.12 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.55,-6.78 -4.16,-5.65 C-4.16,-5.65 -5.67,-7.13 -5.67,-7.13 C-4.81,-8.35 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.85,-0.89 6.49,0.02 C6.13,0.94 5.16,2.35 4.62,3.11c " + android:valueTo="M3.98 4.01 C3.98,4.01 2.56,2.54 2.56,2.54 C3.34,1.49 4.41,0.01 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -4.01,-6.61 -4.61,-4.61 C-4.61,-4.61 -6.24,-6.22 -6.24,-6.22 C-5.27,-8.11 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.86,-0.99 6.49,0.02 C6.12,1.04 4.57,3.23 3.98,4.01c " + 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_1"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="100" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M1.8 -1.24 C2.03,-1.47 2.21,-1.76 2.33,-2.05 C2.44,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.25,-5.21 -1.57,-5.02 -1.77,-4.78 C-1.77,-4.78 1.8,-1.24 1.8,-1.24c " + android:valueTo="M0.87 -0.65 C1.56,-0.93 2.02,-1.3 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.63,-5.06 -2.14,-4.44 -2.33,-3.91 C-2.11,-3.68 0.66,-0.85 0.87,-0.65c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="67" + android:propertyName="pathData" + android:startOffset="100" + android:valueFrom="M0.87 -0.65 C1.56,-0.93 2.02,-1.3 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.63,-5.06 -2.14,-4.44 -2.33,-3.91 C-2.11,-3.68 0.66,-0.85 0.87,-0.65c " + android:valueTo="M-0.29 -0.52 C0.81,-0.39 1.86,-0.95 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.93,-4.94 -2.6,-3.89 -2.49,-2.82 C-2.18,-2.46 -0.67,-0.84 -0.29,-0.52c " + 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="167" + android:propertyName="pathData" + android:startOffset="167" + android:valueFrom="M-0.29 -0.52 C0.81,-0.39 1.86,-0.95 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.93,-4.94 -2.6,-3.89 -2.49,-2.82 C-2.18,-2.46 -0.67,-0.84 -0.29,-0.52c " + android:valueTo="M-0.96 -0.67 C0.37,-0.16 1.79,-0.77 2.33,-2.05 C2.45,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-2.08,-4.88 -2.84,-3.58 -2.35,-2.11 C-2.16,-1.55 -1.58,-0.91 -0.96,-0.67c " + 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="167" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c " + android:valueTo="M8.5 10.6 C8.5,10.6 8.51,10.6 8.51,10.6 C8.51,10.6 9.91,9.2 9.91,9.2 C9.91,9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c " + android:valueType="pathType"> + <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:duration="350" + 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="12" + 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-5.05 -2.1 C-5.05,-1.06 -4.64,-0.4 -4.23,0.3 C-3.81,1 -3.28,1.75 -2.65,2.55 C-2.22,3.1 -1.78,3.71 -1.35,4.39 C-0.92,5.06 -0.39,5.6 0.01,6.43 C0.21,5.95 0.61,5.27 0.98,4.72 C1.44,4.01 2.13,3.09 2.55,2.54 C2.94,2.95 3.54,3.56 3.98,4 C3.47,4.64 2.65,5.82 2.1,6.77 C1.66,7.55 1.28,8.44 0.98,9.26 C0.88,9.55 0.77,9.67 0.59,9.81 C0.44,9.93 0.28,9.99 0,9.99 C-0.28,9.99 -0.48,9.89 -0.68,9.66 C-0.88,9.44 -1.03,9.18 -1.15,8.9 C-1.45,8.07 -1.68,7.43 -2.17,6.65 C-2.67,5.88 -3.27,4.87 -4.15,3.79 C-4.88,2.75 -5.67,1.6 -6.21,0.62 C-6.74,-0.36 -7,-1.76 -7,-3.01 C-7,-4.08 -6.77,-5.28 -6.23,-6.23 C-5.75,-5.73 -4.92,-4.92 -4.61,-4.61 C-4.87,-3.95 -5.06,-2.93 -5.05,-2.1c " /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M5.18 2.33 C5.18,2.33 3.75,0.88 3.75,0.88 C4.13,0.29 4.45,-0.23 4.68,-0.84 C4.9,-1.45 5,-2.2 5,-3 C5,-4.38 4.51,-5.56 3.54,-6.54 C2.56,-7.51 1.38,-8 0,-8 C-0.7,-8 -1.36,-7.89 -1.99,-7.61 C-2.61,-7.34 -3.16,-6.94 -3.61,-6.46 C-3.61,-6.46 -5.07,-7.86 -5.07,-7.86 C-4.42,-8.54 -3.65,-9.09 -2.79,-9.46 C-1.94,-9.84 -1,-10 0,-10 C1.93,-10 3.6,-9.32 4.97,-7.96 C6.33,-6.59 7,-4.93 7,-3 C7,-1.85 6.84,-0.81 6.49,0.02 C6.14,0.86 5.68,1.58 5.18,2.33c " /> + <path + android:name="_R_G_L_0_G_D_1_P_1" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M1.8 -1.24 C2.03,-1.47 2.21,-1.76 2.33,-2.05 C2.44,-2.34 2.5,-2.67 2.5,-3.02 C2.5,-3.72 2.24,-4.3 1.76,-4.78 C1.27,-5.27 0.7,-5.5 0,-5.5 C-0.33,-5.5 -0.65,-5.44 -0.95,-5.32 C-1.25,-5.21 -1.57,-5.02 -1.77,-4.78 C-1.77,-4.78 1.8,-1.24 1.8,-1.24c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="1" + android:fillColor="#edf2eb" + android:fillType="nonZero" + android:pathData=" M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_mic_access_off.xml b/packages/SystemUI/res/drawable/qs_mic_access_off.xml new file mode 100644 index 000000000000..63a94443c255 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_mic_access_off.xml @@ -0,0 +1,212 @@ +<?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 + --> +<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_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="167" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M5.05 4.43 C3.98,5.25 3.08,5.53 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.58,3.77 3.61,2.99 C3.61,2.99 5.05,4.43 5.05,4.43c " + android:valueTo="M5.02 4.5 C3.59,5.47 2.79,5.64 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.53,3.83 3.58,3.07 C3.58,3.07 5.02,4.5 5.02,4.5c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,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="44" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M2.31 1.86 C2.31,1.86 1.01,0.55 0.75,0.31 C1.7,0.27 1.7,-0.7 1.7,-0.7 C1.7,-0.7 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-0.66 -0.29,-0.66 C-0.29,-0.66 -2.31,-2.67 -2.31,-2.67 C-2.31,-2.67 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.7,-0.33 3.64,-0.03 C3.51,0.52 3.23,1.19 2.31,1.86c " + android:valueTo="M2.86 1.48 C2.86,1.48 1.58,0.21 1.39,0.03 C1.77,-0.26 1.7,-1.03 1.7,-1.03 C1.7,-1.03 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-1.51 -0.29,-1.51 C-0.29,-1.51 -2.31,-3.5 -2.31,-3.5 C-2.31,-3.5 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.44 3.65,-0.17 C3.61,0.19 3.36,1.05 2.86,1.48c " + 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="39" + android:propertyName="pathData" + android:startOffset="44" + android:valueFrom="M2.86 1.48 C2.86,1.48 1.58,0.21 1.39,0.03 C1.77,-0.26 1.7,-1.03 1.7,-1.03 C1.7,-1.03 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-1.51 -0.29,-1.51 C-0.29,-1.51 -2.31,-3.5 -2.31,-3.5 C-2.31,-3.5 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.44 3.65,-0.17 C3.61,0.19 3.36,1.05 2.86,1.48c " + android:valueTo="M3.12 0.99 C3.12,0.99 1.83,-0.26 1.71,-0.38 C1.72,-0.81 1.7,-1.31 1.7,-1.31 C1.7,-1.31 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-2.26 -0.29,-2.26 C-0.29,-2.26 -2.31,-4.23 -2.31,-4.23 C-2.31,-4.23 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.67,-0.54 3.66,-0.3 C3.63,0.12 3.38,0.7 3.12,0.99c " + 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="83" + android:valueFrom="M3.12 0.99 C3.12,0.99 1.83,-0.26 1.71,-0.38 C1.72,-0.81 1.7,-1.31 1.7,-1.31 C1.7,-1.31 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-2.26 -0.29,-2.26 C-0.29,-2.26 -2.31,-4.23 -2.31,-4.23 C-2.31,-4.23 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.67,-0.54 3.66,-0.3 C3.63,0.12 3.38,0.7 3.12,0.99c " + android:valueTo="M3.5 -0.05 C3.5,-0.05 1.7,-1.85 1.7,-1.85 C1.7,-1.85 1.7,-1.92 1.7,-1.92 C1.7,-1.92 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.3,-3.85 -0.3,-3.85 C-0.3,-3.85 -2.3,-5.85 -2.3,-5.85 C-2.3,-5.85 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.65 3.64,-0.5 C3.6,-0.35 3.55,-0.2 3.5,-0.05c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,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="167" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M5.04 4.45 C5.04,4.45 3.59,3.02 3.59,3.02 C4.82,1.94 5.31,0.96 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.51,3.17 5.04,4.45c " + android:valueTo="M6.45 2.95 C6.45,2.95 5,1.5 5,1.5 C5.23,1.12 5.41,0.72 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.85,2.37 6.45,2.95c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,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="67" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C-0.4,2.17 -0.94,1.89 -1.41,1.42 C-1.89,0.94 -2.18,0.37 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c " + android:valueTo="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C-0.4,2.17 -0.94,1.89 -1.41,1.42 C-1.89,0.94 -2.18,0.37 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" /> + </aapt:attr> + </objectAnimator> + <objectAnimator + android:duration="100" + android:propertyName="pathData" + android:startOffset="67" + android:valueFrom="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C-0.4,2.17 -0.94,1.89 -1.41,1.42 C-1.89,0.94 -2.18,0.37 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c " + android:valueTo="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C0.09,2.2 -0.55,1.45 -1.02,0.98 C-1.5,0.5 -2.23,-0.14 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 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="17" + 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> + </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="250" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c " + android:valueTo="M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.2,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_0_G" + android:translateX="12" + android:translateY="12"> + <path + android:name="_R_G_L_0_G_D_0_P_0" + android:fillAlpha="1" + android:fillColor="#edf2eb" + android:fillType="nonZero" + android:pathData=" M5.05 4.43 C3.98,5.25 3.08,5.53 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.58,3.77 3.61,2.99 C3.61,2.99 5.05,4.43 5.05,4.43c " /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#edf2eb" + android:fillType="nonZero" + android:pathData=" M2.31 1.86 C2.31,1.86 1.01,0.55 0.75,0.31 C1.7,0.27 1.7,-0.7 1.7,-0.7 C1.7,-0.7 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-0.66 -0.29,-0.66 C-0.29,-0.66 -2.31,-2.67 -2.31,-2.67 C-2.31,-2.67 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.7,-0.33 3.64,-0.03 C3.51,0.52 3.23,1.19 2.31,1.86c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="1" + android:fillColor="#edf2eb" + android:fillType="nonZero" + android:pathData=" M5.04 4.45 C5.04,4.45 3.59,3.02 3.59,3.02 C4.82,1.94 5.31,0.96 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.51,3.17 5.04,4.45c " /> + <path + android:name="_R_G_L_0_G_D_3_P_0" + android:fillAlpha="1" + android:fillColor="#edf2eb" + android:fillType="nonZero" + android:pathData=" M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C-0.4,2.17 -0.94,1.89 -1.41,1.42 C-1.89,0.94 -2.18,0.37 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c " /> + <path + android:name="_R_G_L_0_G_D_4_P_0" + android:fillAlpha="0" + android:fillColor="#edf2eb" + android:fillType="nonZero" + android:pathData=" M-9.89 -7.81 C-9.89,-7.81 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 -8.49,-9.21 -8.49,-9.21 C-8.49,-9.21 -9.89,-7.81 -9.89,-7.81c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/qs_mic_access_on.xml b/packages/SystemUI/res/drawable/qs_mic_access_on.xml new file mode 100644 index 000000000000..b485f06d1bf2 --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_mic_access_on.xml @@ -0,0 +1,212 @@ +<?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 + --> +<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_0_P_0"> + <aapt:attr name="android:animation"> + <set android:ordering="together"> + <objectAnimator + android:duration="250" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M5.02 4.5 C3.59,5.47 2.79,5.64 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.53,3.83 3.58,3.07 C3.58,3.07 5.02,4.5 5.02,4.5c " + android:valueTo="M5.05 4.43 C3.98,5.25 3.08,5.53 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.58,3.77 3.61,2.99 C3.61,2.99 5.05,4.43 5.05,4.43c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 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="125" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M3.5 -0.05 C3.5,-0.05 1.7,-1.85 1.7,-1.85 C1.7,-1.85 1.7,-1.92 1.7,-1.92 C1.7,-1.92 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.3,-3.85 -0.3,-3.85 C-0.3,-3.85 -2.3,-5.85 -2.3,-5.85 C-2.3,-5.85 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.65 3.64,-0.5 C3.6,-0.35 3.55,-0.2 3.5,-0.05c " + android:valueTo="M3.12 0.99 C3.12,0.99 1.83,-0.26 1.71,-0.38 C1.72,-0.81 1.7,-1.31 1.7,-1.31 C1.7,-1.31 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-2.26 -0.29,-2.26 C-0.29,-2.26 -2.31,-4.23 -2.31,-4.23 C-2.31,-4.23 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.67,-0.54 3.66,-0.3 C3.63,0.12 3.38,0.7 3.12,0.99c " + 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="58" + android:propertyName="pathData" + android:startOffset="125" + android:valueFrom="M3.12 0.99 C3.12,0.99 1.83,-0.26 1.71,-0.38 C1.72,-0.81 1.7,-1.31 1.7,-1.31 C1.7,-1.31 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-2.26 -0.29,-2.26 C-0.29,-2.26 -2.31,-4.23 -2.31,-4.23 C-2.31,-4.23 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.67,-0.54 3.66,-0.3 C3.63,0.12 3.38,0.7 3.12,0.99c " + android:valueTo="M2.86 1.48 C2.86,1.48 1.58,0.21 1.39,0.03 C1.77,-0.26 1.7,-1.03 1.7,-1.03 C1.7,-1.03 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-1.51 -0.29,-1.51 C-0.29,-1.51 -2.31,-3.5 -2.31,-3.5 C-2.31,-3.5 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.44 3.65,-0.17 C3.61,0.19 3.36,1.05 2.86,1.48c " + 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="67" + android:propertyName="pathData" + android:startOffset="183" + android:valueFrom="M2.86 1.48 C2.86,1.48 1.58,0.21 1.39,0.03 C1.77,-0.26 1.7,-1.03 1.7,-1.03 C1.7,-1.03 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-1.51 -0.29,-1.51 C-0.29,-1.51 -2.31,-3.5 -2.31,-3.5 C-2.31,-3.5 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.44 3.65,-0.17 C3.61,0.19 3.36,1.05 2.86,1.48c " + android:valueTo="M2.42 1.81 C2.42,1.81 1.16,0.52 0.9,0.28 C1.7,0.14 1.7,-0.7 1.7,-0.7 C1.7,-0.7 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.29,-0.66 -0.29,-0.66 C-0.29,-0.66 -2.31,-2.67 -2.31,-2.67 C-2.31,-2.67 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.7,-0.33 3.64,-0.03 C3.58,0.27 3.22,1.2 2.42,1.81c " + 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> + </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="250" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M6.45 2.95 C6.45,2.95 5,1.5 5,1.5 C5.23,1.12 5.41,0.72 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.85,2.37 6.45,2.95c " + android:valueTo="M5.04 4.43 C5.04,4.43 3.59,2.98 3.59,2.98 C4.61,2.09 5.41,0.72 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.19,3.44 5.04,4.43c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 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="250" + android:propertyName="pathData" + android:startOffset="0" + android:valueFrom="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C0.09,2.2 -0.55,1.45 -1.02,0.98 C-1.5,0.5 -2.23,-0.14 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c " + android:valueTo="M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C-0.4,2.17 -0.94,1.89 -1.41,1.42 C-1.89,0.94 -2.18,0.37 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c " + android:valueType="pathType"> + <aapt:attr name="android:interpolator"> + <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.1,1 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="400" + 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="400" + 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="pathData" + android:startOffset="0" + android:valueFrom="M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c " + android:valueTo="M8.5 10.6 C8.5,10.6 8.51,10.6 8.51,10.6 C8.51,10.6 9.91,9.2 9.91,9.2 C9.91,9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c " + android:valueType="pathType"> + <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: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_0_G" + android:translateX="12" + 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=" M5.02 4.5 C3.59,5.47 2.79,5.64 2.53,5.73 C2.26,5.81 1.98,5.87 1.7,5.9 C1.7,5.9 1.7,9 1.7,9 C1.7,9 -0.3,9 -0.3,9 C-0.3,9 -0.3,5.9 -0.3,5.9 C-2.02,5.65 -3.44,4.88 -4.59,3.59 C-5.73,2.3 -6.3,0.77 -6.3,-1 C-6.3,-1 -4.3,-1 -4.3,-1 C-4.3,0.38 -3.81,1.56 -2.84,2.54 C-1.86,3.51 -0.68,4 0.7,4 C0.88,4 1.06,3.99 1.23,3.96 C1.39,3.94 2.53,3.83 3.58,3.07 C3.58,3.07 5.02,4.5 5.02,4.5c " /> + <path + android:name="_R_G_L_0_G_D_1_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M3.5 -0.05 C3.5,-0.05 1.7,-1.85 1.7,-1.85 C1.7,-1.85 1.7,-1.92 1.7,-1.92 C1.7,-1.92 1.7,-7 1.7,-7 C1.7,-7.28 1.61,-7.52 1.41,-7.71 C1.22,-7.9 0.98,-8 0.7,-8 C0.42,-8 0.18,-7.9 -0.01,-7.71 C-0.2,-7.52 -0.3,-7.28 -0.3,-7 C-0.3,-7 -0.3,-3.85 -0.3,-3.85 C-0.3,-3.85 -2.3,-5.85 -2.3,-5.85 C-2.3,-5.85 -2.3,-7 -2.3,-7 C-2.3,-7.83 -2.01,-8.54 -1.42,-9.12 C-0.84,-9.71 -0.13,-10 0.7,-10 C1.53,-10 2.24,-9.71 2.83,-9.12 C3.41,-8.54 3.7,-7.83 3.7,-7 C3.7,-7 3.7,-1 3.7,-1 C3.7,-0.82 3.68,-0.65 3.64,-0.5 C3.6,-0.35 3.55,-0.2 3.5,-0.05c " /> + <path + android:name="_R_G_L_0_G_D_2_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M6.45 2.95 C6.45,2.95 5,1.5 5,1.5 C5.23,1.12 5.41,0.72 5.53,0.3 C5.64,-0.12 5.7,-0.55 5.7,-1 C5.7,-1 7.7,-1 7.7,-1 C7.7,-0.27 7.59,0.43 7.38,1.09 C7.16,1.75 6.85,2.37 6.45,2.95c " /> + <path + android:name="_R_G_L_0_G_D_3_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M-0.29 -0.71 C-0.29,-0.42 -0.19,-0.19 0,0.01 C0.19,0.2 0.43,0.29 0.71,0.29 C0.76,0.31 0.75,0.31 0.78,0.3 C1.14,0.67 1.92,1.43 2.34,1.85 C2.03,2.04 1.55,2.29 0.71,2.29 C0.55,2.29 0.39,2.28 0.23,2.26 C0.09,2.2 -0.55,1.45 -1.02,0.98 C-1.5,0.5 -2.23,-0.14 -2.26,-0.28 C-2.28,-0.42 -2.29,-0.56 -2.29,-0.71 C-2.29,-0.71 -2.31,-2.72 -2.31,-2.72 C-2.31,-2.72 -0.29,-0.71 -0.29,-0.71c " /> + <path + android:name="_R_G_L_0_G_D_4_P_0" + android:fillAlpha="1" + android:fillColor="#ffffff" + android:fillType="nonZero" + android:pathData=" M8.5 10.6 C8.5,10.6 -9.9,-7.8 -9.9,-7.8 C-9.9,-7.8 -8.5,-9.2 -8.5,-9.2 C-8.5,-9.2 9.9,9.2 9.9,9.2 C9.9,9.2 8.5,10.6 8.5,10.6c " /> + </group> + </group> + <group android:name="time_group" /> + </vector> + </aapt:attr> +</animated-vector> diff --git a/packages/SystemUI/res/drawable/screenshot_edit_background.xml b/packages/SystemUI/res/drawable/screenshot_edit_background.xml index ff5c62e1600b..a1185a2d5479 100644 --- a/packages/SystemUI/res/drawable/screenshot_edit_background.xml +++ b/packages/SystemUI/res/drawable/screenshot_edit_background.xml @@ -17,7 +17,7 @@ <!-- Long screenshot edit FAB background --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:color="?android:textColorPrimary"> + android:color="@color/overlay_button_ripple"> <item android:id="@android:id/background"> <shape android:shape="rectangle"> <solid android:color="?androidprv:attr/colorAccentPrimary"/> diff --git a/packages/SystemUI/res/layout/auth_container_view.xml b/packages/SystemUI/res/layout/auth_container_view.xml index 3db01a4e7f3a..2bd2e640127e 100644 --- a/packages/SystemUI/res/layout/auth_container_view.xml +++ b/packages/SystemUI/res/layout/auth_container_view.xml @@ -23,7 +23,6 @@ android:id="@+id/background" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/biometric_dialog_dim_color" android:contentDescription="@string/biometric_dialog_empty_space_description"/> <View diff --git a/packages/SystemUI/res/layout/brightness_mirror_container.xml b/packages/SystemUI/res/layout/brightness_mirror_container.xml index ac90db3e5e19..1bf45aad8906 100644 --- a/packages/SystemUI/res/layout/brightness_mirror_container.xml +++ b/packages/SystemUI/res/layout/brightness_mirror_container.xml @@ -23,7 +23,6 @@ android:background="@drawable/brightness_mirror_background" android:layout_gravity="center_vertical" android:layout_margin="8dp" - android:padding="@dimen/rounded_slider_background_padding" android:gravity="center" android:visibility="invisible"> diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml index 99a5a2e904f6..1a1fc75a41a1 100644 --- a/packages/SystemUI/res/layout/clipboard_overlay.xml +++ b/packages/SystemUI/res/layout/clipboard_overlay.xml @@ -115,6 +115,7 @@ android:autoSizeMinTextSize="@dimen/clipboard_overlay_min_font" android:autoSizeMaxTextSize="@dimen/clipboard_overlay_max_font" android:textColor="?attr/overlayButtonTextColor" + android:textColorLink="?attr/overlayButtonTextColor" android:background="?androidprv:attr/colorAccentSecondary" android:layout_width="@dimen/clipboard_preview_size" android:layout_height="@dimen/clipboard_preview_size"/> diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml index d0f4903a3421..70a770912c7f 100644 --- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml +++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml @@ -70,15 +70,32 @@ android:visibility="gone" android:contentDescription="@string/dream_overlay_status_bar_wifi_off" /> - <com.android.systemui.dreams.DreamOverlayDotImageView + <ImageView + android:id="@+id/dream_overlay_mic_off" + android:layout_width="@dimen/dream_overlay_grey_chip_width" + android:layout_height="match_parent" + android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin" + android:src="@drawable/dream_overlay_mic_off" + android:visibility="gone" + android:contentDescription="@string/dream_overlay_status_bar_mic_off" /> + + <ImageView + android:id="@+id/dream_overlay_camera_off" + android:layout_width="@dimen/dream_overlay_grey_chip_width" + android:layout_height="match_parent" + android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin" + android:src="@drawable/dream_overlay_camera_off" + android:visibility="gone" + android:contentDescription="@string/dream_overlay_status_bar_camera_off" /> + + <ImageView android:id="@+id/dream_overlay_camera_mic_off" - android:layout_width="@dimen/dream_overlay_camera_mic_off_indicator_size" - android:layout_height="@dimen/dream_overlay_camera_mic_off_indicator_size" - android:layout_gravity="center_vertical" + android:layout_width="@dimen/dream_overlay_grey_chip_width" + android:layout_height="match_parent" android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin" + android:src="@drawable/dream_overlay_mic_and_camera_off" android:visibility="gone" - android:contentDescription="@string/dream_overlay_status_bar_camera_mic_off" - app:dotColor="@color/dream_overlay_camera_mic_off_dot_color" /> + android:contentDescription="@string/dream_overlay_status_bar_camera_mic_off" /> </LinearLayout> </com.android.systemui.dreams.DreamOverlayStatusBarView> diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml index e47eed9ea04a..d27fa192e741 100644 --- a/packages/SystemUI/res/layout/keyguard_status_bar.xml +++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml @@ -60,9 +60,8 @@ </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer> <FrameLayout android:id="@+id/system_icons_container" - android:layout_width="0dp" + android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_weight="1" android:layout_marginEnd="@dimen/status_bar_padding_end" android:gravity="center_vertical|end"> <include layout="@layout/system_icons" /> diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml index 1efb4796b5b7..93c16e4e119d 100644 --- a/packages/SystemUI/res/layout/media_output_dialog.xml +++ b/packages/SystemUI/res/layout/media_output_dialog.xml @@ -42,12 +42,35 @@ android:layout_height="wrap_content" android:paddingStart="12dp" android:orientation="vertical"> - <ImageView - android:id="@+id/app_source_icon" - android:layout_width="20dp" - android:layout_height="20dp" + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" android:gravity="center_vertical" - android:importantForAccessibility="no"/> + android:orientation="horizontal"> + <ImageView + android:id="@+id/app_source_icon" + android:layout_width="20dp" + android:layout_height="20dp" + android:gravity="center_vertical" + android:importantForAccessibility="no"/> + + <Space + android:layout_weight="1" + android:layout_width="0dp" + android:layout_height="match_parent"/> + + <ImageView + android:id="@+id/broadcast_icon" + android:src="@drawable/settings_input_antenna" + android:contentDescription="@string/broadcasting_description_is_broadcasting" + android:layout_width="48dp" + android:layout_height="48dp" + android:padding="12dp" + android:gravity="center_vertical" + android:clickable="true" + android:focusable="true" + android:visibility="gone"/> + </LinearLayout> <TextView android:id="@+id/header_title" android:layout_width="wrap_content" @@ -89,8 +112,7 @@ android:id="@+id/list_result" android:scrollbars="vertical" android:layout_width="match_parent" - android:layout_height="wrap_content" - android:overScrollMode="never"/> + android:layout_height="wrap_content"/> </LinearLayout> <LinearLayout diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml index 9c49607d414e..c526d9cc8dd3 100644 --- a/packages/SystemUI/res/layout/media_session_view.xml +++ b/packages/SystemUI/res/layout/media_session_view.xml @@ -150,7 +150,7 @@ <!-- See comment in media_session_collapsed.xml for how these barriers are used --> <androidx.constraintlayout.widget.Barrier - android:id="@+id/media_action_barrier" + android:id="@+id/media_action_barrier_start" android:layout_width="0dp" android:layout_height="0dp" android:orientation="vertical" @@ -172,6 +172,7 @@ app:layout_constraintStart_toStartOf="parent" /> + <!-- This barrier is used in expanded view to constrain the bottom row of actions --> <androidx.constraintlayout.widget.Barrier android:id="@+id/media_action_barrier_top" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout/notification_icon_area.xml b/packages/SystemUI/res/layout/notification_icon_area.xml index fa696cc1f54c..aadfae8c5aed 100644 --- a/packages/SystemUI/res/layout/notification_icon_area.xml +++ b/packages/SystemUI/res/layout/notification_icon_area.xml @@ -14,18 +14,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<com.android.keyguard.AlphaOptimizedLinearLayout +<com.android.systemui.statusbar.phone.NotificationIconContainer xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/notification_icon_area_inner" - android:layout_width="match_parent" + android:id="@+id/notificationIcons" + android:layout_width="wrap_content" android:layout_height="match_parent" - android:clipChildren="false"> - <com.android.systemui.statusbar.phone.NotificationIconContainer - android:id="@+id/notificationIcons" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_alignParentStart="true" - android:gravity="center_vertical" - android:orientation="horizontal" - android:clipChildren="false"/> -</com.android.keyguard.AlphaOptimizedLinearLayout>
\ No newline at end of file + android:clipChildren="false"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_space_activity.xml b/packages/SystemUI/res/layout/people_space_activity.xml index 7102375a89bf..f45cc7c464d5 100644 --- a/packages/SystemUI/res/layout/people_space_activity.xml +++ b/packages/SystemUI/res/layout/people_space_activity.xml @@ -13,103 +13,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<LinearLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/top_level" + android:id="@+id/container" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:padding="8dp"> - <TextView - android:id="@+id/select_conversation_title" - android:text="@string/select_conversation_title" - android:gravity="center" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" - android:textSize="24sp"/> - - <TextView - android:id="@+id/select_conversation" - android:text="@string/select_conversation_text" - android:gravity="center" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_gravity="center_horizontal" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" - android:textColor="?android:attr/textColorPrimary" - android:textSize="16sp" - android:paddingVertical="24dp" - android:paddingHorizontal="48dp"/> - - <androidx.core.widget.NestedScrollView - android:id="@+id/scroll_view" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <LinearLayout - android:id="@+id/scroll_layout" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="16dp" - android:orientation="vertical"> - - <LinearLayout - android:id="@+id/priority" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginBottom="35dp"> - <TextView - android:id="@+id/priority_header" - android:text="@string/priority_conversations" - android:layout_width="wrap_content" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" - android:textColor="?androidprv:attr/colorAccentPrimaryVariant" - android:textSize="14sp" - android:paddingStart="16dp" - android:layout_height="wrap_content"/> - - <LinearLayout - android:id="@+id/priority_tiles" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:orientation="vertical" - android:background="@drawable/rounded_bg_full_large_radius" - android:clipToOutline="true"> - </LinearLayout> - </LinearLayout> - - <LinearLayout - android:id="@+id/recent" - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <TextView - android:id="@+id/recent_header" - android:gravity="start" - android:text="@string/recent_conversations" - android:layout_width="wrap_content" - android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" - android:textColor="?androidprv:attr/colorAccentPrimaryVariant" - android:textSize="14sp" - android:paddingStart="16dp" - android:layout_height="wrap_content"/> - - <LinearLayout - android:id="@+id/recent_tiles" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginTop="10dp" - android:orientation="vertical" - android:background="@drawable/rounded_bg_full_large_radius" - android:clipToOutline="true"> - </LinearLayout> - </LinearLayout> - </LinearLayout> - </androidx.core.widget.NestedScrollView> -</LinearLayout>
\ No newline at end of file + android:layout_height="match_parent"> + <!-- The content of people_space_activity_(no|with)_conversations.xml will be added here at + runtime depending on the number of conversations to show. --> +</FrameLayout> diff --git a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml index 2e9ff07caed9..e929169cfe3d 100644 --- a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml +++ b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml @@ -16,7 +16,7 @@ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:id="@+id/top_level" + android:id="@+id/top_level_no_conversations" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="24dp" diff --git a/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml new file mode 100644 index 000000000000..2384963c44db --- /dev/null +++ b/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml @@ -0,0 +1,115 @@ +<!-- + ~ 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" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:id="@+id/top_level_with_conversations" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="8dp"> + <TextView + android:id="@+id/select_conversation_title" + android:text="@string/select_conversation_title" + android:gravity="center" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" + android:textColor="?android:attr/textColorPrimary" + android:textSize="24sp"/> + + <TextView + android:id="@+id/select_conversation" + android:text="@string/select_conversation_text" + android:gravity="center" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem" + android:textColor="?android:attr/textColorPrimary" + android:textSize="16sp" + android:paddingVertical="24dp" + android:paddingHorizontal="48dp"/> + + <androidx.core.widget.NestedScrollView + android:id="@+id/scroll_view" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <LinearLayout + android:id="@+id/scroll_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:orientation="vertical"> + + <LinearLayout + android:id="@+id/priority" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="35dp"> + <TextView + android:id="@+id/priority_header" + android:text="@string/priority_conversations" + android:layout_width="wrap_content" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" + android:textColor="?androidprv:attr/colorAccentPrimaryVariant" + android:textSize="14sp" + android:paddingStart="16dp" + android:layout_height="wrap_content"/> + + <LinearLayout + android:id="@+id/priority_tiles" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:orientation="vertical" + android:background="@drawable/rounded_bg_full_large_radius" + android:clipToOutline="true"> + </LinearLayout> + </LinearLayout> + + <LinearLayout + android:id="@+id/recent" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + <TextView + android:id="@+id/recent_header" + android:gravity="start" + android:text="@string/recent_conversations" + android:layout_width="wrap_content" + android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title" + android:textColor="?androidprv:attr/colorAccentPrimaryVariant" + android:textSize="14sp" + android:paddingStart="16dp" + android:layout_height="wrap_content"/> + + <LinearLayout + android:id="@+id/recent_tiles" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="10dp" + android:orientation="vertical" + android:background="@drawable/rounded_bg_full_large_radius" + android:clipToOutline="true"> + </LinearLayout> + </LinearLayout> + </LinearLayout> + </androidx.core.widget.NestedScrollView> +</LinearLayout>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/people_space_tile_view.xml b/packages/SystemUI/res/layout/people_space_tile_view.xml index 2a2c35dde841..b0599caae6df 100644 --- a/packages/SystemUI/res/layout/people_space_tile_view.xml +++ b/packages/SystemUI/res/layout/people_space_tile_view.xml @@ -37,8 +37,8 @@ <ImageView android:id="@+id/tile_view_person_icon" - android:layout_width="52dp" - android:layout_height="52dp" /> + android:layout_width="@dimen/avatar_size_for_medium" + android:layout_height="@dimen/avatar_size_for_medium" /> <LinearLayout android:orientation="horizontal" diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index deab1ebd6507..e281511140c7 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -47,52 +47,63 @@ android:paddingStart="@dimen/status_bar_padding_start" android:paddingEnd="@dimen/status_bar_padding_end" android:paddingTop="@dimen/status_bar_padding_top" - android:orientation="horizontal" - > + android:orientation="horizontal"> + + <!-- Container for the entire start half of the status bar. It will always use the same + width, independent of the number of visible children and sub-children. --> <FrameLayout + android:id="@+id/status_bar_start_side_container" android:layout_height="match_parent" android:layout_width="0dp" android:layout_weight="1"> - <include layout="@layout/heads_up_status_bar_layout" /> + <!-- Container that is wrapped around the views on the start half of the status bar. + Its width will change with the number of visible children and sub-children. + It is useful when we want to know the visible bounds of the content. --> + <FrameLayout + android:id="@+id/status_bar_start_side_content" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clipChildren="false"> - <!-- The alpha of the left side is controlled by PhoneStatusBarTransitions, and the - individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK and - DISABLE_NOTIFICATION_ICONS, respectively --> - <LinearLayout - android:id="@+id/status_bar_left_side" - android:layout_height="match_parent" - android:layout_width="match_parent" - android:clipChildren="false" - > - <ViewStub - android:id="@+id/operator_name" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout="@layout/operator_name" /> + <include layout="@layout/heads_up_status_bar_layout" /> - <com.android.systemui.statusbar.policy.Clock - android:id="@+id/clock" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:textAppearance="@style/TextAppearance.StatusBar.Clock" - android:singleLine="true" - android:paddingStart="@dimen/status_bar_left_clock_starting_padding" - android:paddingEnd="@dimen/status_bar_left_clock_end_padding" - android:gravity="center_vertical|start" - /> - - <include layout="@layout/ongoing_call_chip" /> - - <com.android.systemui.statusbar.AlphaOptimizedFrameLayout - android:id="@+id/notification_icon_area" - android:layout_width="0dp" - android:layout_height="match_parent" - android:layout_weight="1" - android:orientation="horizontal" - android:clipChildren="false"/> + <!-- The alpha of the start side is controlled by PhoneStatusBarTransitions, and the + individual views are controlled by StatusBarManager disable flags DISABLE_CLOCK + and DISABLE_NOTIFICATION_ICONS, respectively --> + <LinearLayout + android:id="@+id/status_bar_start_side_except_heads_up" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:clipChildren="false"> + <ViewStub + android:id="@+id/operator_name" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout="@layout/operator_name" /> + + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + android:singleLine="true" + android:paddingStart="@dimen/status_bar_left_clock_starting_padding" + android:paddingEnd="@dimen/status_bar_left_clock_end_padding" + android:gravity="center_vertical|start" + /> + + <include layout="@layout/ongoing_call_chip" /> - </LinearLayout> + <com.android.systemui.statusbar.AlphaOptimizedFrameLayout + android:id="@+id/notification_icon_area" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:orientation="horizontal" + android:clipChildren="false"/> + + </LinearLayout> + </FrameLayout> </FrameLayout> <!-- Space should cover the notch (if it exists) and let other views lay out around it --> @@ -103,42 +114,57 @@ android:gravity="center_horizontal|center_vertical" /> - <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" + <!-- Container for the entire end half of the status bar. It will always use the same + width, independent of the number of visible children and sub-children. --> + <FrameLayout + android:id="@+id/status_bar_end_side_container" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" - android:orientation="horizontal" - android:gravity="center_vertical|end" - > - - <com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer - android:id="@+id/user_switcher_container" + android:clipChildren="false"> + + <!-- Container that is wrapped around the views on the end half of the + status bar. Its width will change with the number of visible children and + sub-children. + It is useful when we want know the visible bounds of the content.--> + <com.android.keyguard.AlphaOptimizedLinearLayout + android:id="@+id/status_bar_end_side_content" android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center" + android:layout_height="match_parent" + android:layout_gravity="end" android:orientation="horizontal" - android:paddingTop="4dp" - android:paddingBottom="4dp" - android:paddingStart="8dp" - android:paddingEnd="8dp" - android:layout_marginEnd="16dp" - android:background="@drawable/status_bar_user_chip_bg" - android:visibility="visible" > - <ImageView android:id="@+id/current_user_avatar" - android:layout_width="@dimen/multi_user_avatar_keyguard_size" - android:layout_height="@dimen/multi_user_avatar_keyguard_size" - android:scaleType="centerInside" - android:paddingEnd="4dp" /> - - <TextView android:id="@+id/current_user_name" + android:gravity="center_vertical|end" + android:clipChildren="false"> + + <com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer + android:id="@+id/user_switcher_container" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:textAppearance="@style/TextAppearance.StatusBar.Clock" - /> - </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer> - - <include layout="@layout/system_icons" /> - </com.android.keyguard.AlphaOptimizedLinearLayout> + android:gravity="center" + android:orientation="horizontal" + android:paddingTop="4dp" + android:paddingBottom="4dp" + android:paddingStart="8dp" + android:paddingEnd="8dp" + android:layout_marginEnd="16dp" + android:background="@drawable/status_bar_user_chip_bg" + android:visibility="visible" > + <ImageView android:id="@+id/current_user_avatar" + android:layout_width="@dimen/multi_user_avatar_keyguard_size" + android:layout_height="@dimen/multi_user_avatar_keyguard_size" + android:scaleType="centerInside" + android:paddingEnd="4dp" /> + + <TextView android:id="@+id/current_user_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + /> + </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer> + + <include layout="@layout/system_icons" /> + </com.android.keyguard.AlphaOptimizedLinearLayout> + </FrameLayout> </LinearLayout> <ViewStub diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 4d5bf53eb64a..6423a50fc107 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -18,7 +18,7 @@ --> -<com.android.systemui.statusbar.phone.NotificationPanelView +<com.android.systemui.shade.NotificationPanelView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res-auto" android:id="@+id/notification_panel" @@ -67,7 +67,7 @@ </com.android.keyguard.LockIconView> - <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer + <com.android.systemui.shade.NotificationsQuickSettingsContainer android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" @@ -150,11 +150,11 @@ android:text="@string/tap_again" android:visibility="gone" /> - </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer> + </com.android.systemui.shade.NotificationsQuickSettingsContainer> <FrameLayout android:id="@+id/preview_container" android:layout_width="match_parent" android:layout_height="match_parent"> </FrameLayout> -</com.android.systemui.statusbar.phone.NotificationPanelView> +</com.android.systemui.shade.NotificationPanelView> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 60860bad9c64..86f8ce26ccab 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -18,7 +18,7 @@ --> <!-- This is the notification shade window. --> -<com.android.systemui.statusbar.phone.NotificationShadeWindowView +<com.android.systemui.shade.NotificationShadeWindowView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" @@ -114,4 +114,4 @@ android:importantForAccessibility="no" sysui:ignoreRightInset="true" /> -</com.android.systemui.statusbar.phone.NotificationShadeWindowView> +</com.android.systemui.shade.NotificationShadeWindowView> diff --git a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml index 0f2d372f7158..c2c79cb0f34b 100644 --- a/packages/SystemUI/res/layout/user_switcher_fullscreen.xml +++ b/packages/SystemUI/res/layout/user_switcher_fullscreen.xml @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. --> -<androidx.constraintlayout.widget.ConstraintLayout +<com.android.systemui.user.UserSwitcherRootView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" @@ -68,4 +68,4 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHeight_min="48dp" /> -</androidx.constraintlayout.widget.ConstraintLayout> +</com.android.systemui.user.UserSwitcherRootView> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index f772e75bf653..3d27dfd95760 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Tik weer"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Swiep op om oop te maak"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Druk die onsluitikoon om oop te maak"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Ontsluit met gesig. Druk die ontsluitikoon om oop te maak."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Ontsluit met gesig. Druk om oop te maak."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Gesig is herken. Druk om oop te maak."</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index b3f5d39a3101..78873e529f13 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"እንደገና መታ ያድርጉ"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"ለመክፈት በጣት ወደ ላይ ጠረግ ያድርጉ"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"ለመክፈት የመክፈቻ አዶውን ይጫኑ"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"በመልክ ተከፍቷል። ለመክፈት የመክፈቻ አዶውን ይጫኑ።"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"በመልክ ተከፍቷል። ለመክፈት ይጫኑ።"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"መልክ ተለይቶ ታውቋል። ለመክፈት ይጫኑ።"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 5bc1f89a9a86..98ab7a4b8836 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"انقر مرة أخرى"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"يمكنك الفتح بالتمرير سريعًا لأعلى."</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"اضغط على رمز فتح القفل لفتح قفل الشاشة."</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"تم فتح القفل بالتعرّف على وجهك. لفتح الجهاز، اضغط على رمز فتح القفل."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"تم فتح قفل جهازك عند تقريبه من وجهك. اضغط لفتح الجهاز."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"تم التعرّف على الوجه. اضغط لفتح الجهاز."</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index f411b7d9e533..564fd1b11ff1 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"পুনৰ টিপক"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"খুলিবলৈ ওপৰলৈ ছোৱাইপ কৰক"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"খুলিবলৈ আনলক কৰক চিহ্নটোত টিপক"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"মুখাৱয়বৰ জৰিয়তে আনলক কৰা হৈছে। খুলিবলৈ আনলক কৰক চিহ্নটোত টিপক।"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"মুখাৱয়বৰ জৰিয়তে আনলক কৰা হৈছে। খুলিবলৈ টিপক।"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"মুখাৱয়ব চিনাক্ত কৰা হৈছে। খুলিবলৈ টিপক।"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index fe542c1a79b4..4ce451c86ed1 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Yenidən toxunun"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Açmaq üçün yuxarı sürüşdürün"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"\"Kilidi aç\" ikonasına basıb açın"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Üzlə kilidi açılıb. \"Kilidi aç\" ikonasına basıb açın."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Üz ilə kiliddən çıxarılıb. Açmaq üçün basın."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Üz tanınıb. Açmaq üçün basın."</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 873663bc0e59..e87cc5effbcf 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Dodirnite ponovo"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Prevucite nagore da biste otvorili"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Pritisnite ikonu otključavanja da biste otvorili."</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Otključano je licem. Pritisnite ikonu otključavanja da biste otvorili."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Otključano je licem. Pritisnite da biste otvorili."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Lice je prepoznato. Pritisnite da biste otvorili."</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 7e595c951054..5779cb3ace7e 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Націсніце яшчэ раз"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Каб адкрыць, прагарніце ўверх"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Каб адкрыць, націсніце значок разблакіроўкі"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Твар распазнаны. Для адкрыцця націсніце значок разблакіроўкі"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Разблакіравана распазнаваннем твару. Націсніце, каб адкрыць."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Твар распазнаны. Націсніце, каб адкрыць."</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index d32b85a8623c..1784939b8f2d 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Докоснете отново"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Прекарайте пръст нагоре, за да отключите"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Натиснете иконата за отключване, за да отворите"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Отключено с лице. Натиснете иконата за отключване, за да отворите."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Отключено с лице. Натиснете за отваряне."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Лицето бе разпознато. Натиснете за отваряне."</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 7b5a7d88d42f..dfc8a2b714ad 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"আবার ট্যাপ করুন"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"খোলার জন্য উপরে সোয়াইপ করুন"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"খোলার জন্য আনলক আইকন প্রেস করুন"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ফেসের সাহায্যে আনলক করা হয়েছে। খোলার জন্য আনলক আইকন প্রেস করুন।"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ফেসের সাহায্যে আনলক করা হয়েছে। খোলার জন্য প্রেস করুন।"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ফেস শনাক্ত করা হয়েছে। খোলার জন্য প্রেস করুন।"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index c1d20e3a66b2..1ae04fedbf03 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Ponovo dodirnite"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Prevucite da otvorite"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Pritisnite ikonu za otključavanje da otvorite."</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Otključano licem. Pritisnite ikonu za otklj. da otvorite."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Otključano licem. Pritisnite da otvorite."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Lice prepoznato. Pritisnite da otvorite."</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 137b04733c07..0899d882edec 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Torna a tocar"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Llisca cap amunt per obrir"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Prem la icona de desbloqueig per obrir"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"S\'ha desbloquejat amb la cara. Prem la icona per obrir."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"S\'ha desbloquejat amb la cara. Prem per obrir."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"S\'ha reconegut la cara. Prem per obrir."</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index e0fa698f85aa..db653160c64d 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Znovu klepněte"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Otevřete přejetím prstem nahoru"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Otevřete klepnutím na ikonu odemknutí"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Odemknuto obličejem. Klepněte na ikonu odemknutí."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Odemknuto obličejem. Stisknutím otevřete."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Obličej rozpoznán. Stisknutím otevřete."</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index c52fc71c79d4..c1e364b2745b 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Tryk igen"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Stryg opad for at åbne"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Tryk på oplåsningsikonet for at åbne"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Låst op vha. ansigt. Tryk på oplåsningsikonet for at åbne."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Låst op ved hjælp af ansigt. Tryk for at åbne."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Ansigt genkendt. Tryk for at åbne."</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 815b231307f0..618901cae413 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Noch einmal tippen"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Zum Öffnen nach oben wischen"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Tippe zum Öffnen auf das Symbol „Entsperren“"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Gerät mit dem Gesicht entsperrt. Tippe zum Öffnen auf das Symbol „Entsperren“."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Gerät mit dem Gesicht entsperrt. Tippe zum Öffnen."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Gesicht erkannt. Tippe zum Öffnen."</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 225f7ec20d47..4a15ede13596 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Πατήστε ξανά"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Σύρετε προς τα επάνω για άνοιγμα"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Πατήστε το εικονίδιο ξεκλειδώματος για άνοιγμα"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Ξεκλείδωμα με πρόσωπο. Πατήστε το εικονίδιο ξεκλειδώματος."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Ξεκλείδωμα με αναγνώριση προσώπου. Πατήστε για άνοιγμα."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Το πρόσωπο αναγνωρίστηκε. Πατήστε για άνοιγμα."</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index ccb0a284b2b2..634a9daa2abe 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -307,6 +307,7 @@ <string name="tap_again" msgid="1315420114387908655">"Tap again"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Press the unlock icon to open"</string> + <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"Unlocked by face. Swipe up to open."</string> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Unlocked by face. Press the unlock icon to open."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Unlocked by face. Press to open."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Face recognised. Press to open."</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 4b14e1f68ca6..f0e2208a22d0 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -307,6 +307,7 @@ <string name="tap_again" msgid="1315420114387908655">"Tap again"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Press the unlock icon to open"</string> + <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"Unlocked by face. Swipe up to open."</string> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Unlocked by face. Press the unlock icon to open."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Unlocked by face. Press to open."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Face recognised. Press to open."</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index ccb0a284b2b2..634a9daa2abe 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -307,6 +307,7 @@ <string name="tap_again" msgid="1315420114387908655">"Tap again"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Press the unlock icon to open"</string> + <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"Unlocked by face. Swipe up to open."</string> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Unlocked by face. Press the unlock icon to open."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Unlocked by face. Press to open."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Face recognised. Press to open."</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index ccb0a284b2b2..634a9daa2abe 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -307,6 +307,7 @@ <string name="tap_again" msgid="1315420114387908655">"Tap again"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Press the unlock icon to open"</string> + <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"Unlocked by face. Swipe up to open."</string> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Unlocked by face. Press the unlock icon to open."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Unlocked by face. Press to open."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Face recognised. Press to open."</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 4418230539bd..1418095fbfe6 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -307,6 +307,7 @@ <string name="tap_again" msgid="1315420114387908655">"Tap again"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe up to open"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Press the unlock icon to open"</string> + <string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"Unlocked by face. Swipe up to open."</string> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Unlocked by face. Press the unlock icon to open."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Unlocked by face. Press to open."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Face recognized. Press to open."</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 8140bda8fd4d..26d980169833 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Presiona otra vez"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Presiona el ícono de desbloquear para abrir"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Desbloqueo con rostro. Presiona el ícono para abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Desbloqueo con rostro. Presiona para abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Rostro reconocido. Presiona para abrir."</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 955bb116865e..1876d77f2238 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Toca de nuevo"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Desliza el dedo hacia arriba para abrir"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Pulsa el icono desbloquear para abrir"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Desbloqueado con la cara. Toca el icono de desbloquear para abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Desbloqueado con la cara. Pulsa para abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Cara reconocida. Pulsa para abrir."</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 61ca78df4945..c64fef9cba6b 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Puudutage uuesti"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Pühkige avamiseks üles"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Avamiseks vajutage avamise ikooni"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Avati näoga. Avamiseks vajutage avamise ikooni."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Avati näoga. Avamiseks vajutage."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Nägu tuvastati. Avamiseks vajutage."</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 3b051e52d105..0c1302ebb982 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Sakatu berriro"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Pasatu hatza gora irekitzeko"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Irekitzeko, sakatu desblokeatzeko ikonoa"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Aurpegiaren bidez desblokeatu da. Irekitzeko, sakatu desblokeatzeko ikonoa."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Aurpegiaren bidez desblokeatu da. Sakatu irekitzeko."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Ezagutu da aurpegia. Sakatu irekitzeko."</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 03191a866262..bd0b037bf291 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"دوباره ضربه بزنید"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"برای باز کردن، انگشتتان را تند بهبالا بکشید"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"برای باز کردن، نماد قفلگشایی را فشار دهید"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"قفلْ با چهره باز شد. برای باز کردن، نماد قفلگشایی را فشار دهید."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"قفلْ با چهره باز شد. برای باز کردن، فشار دهید."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"چهره شناسایی شد. برای باز کردن، فشار دهید."</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index ba9f9afc6e9c..329cc44b4e23 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Napauta uudelleen"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Avaa pyyhkäisemällä ylös"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Jatka painamalla lukituksen avauskuvaketta."</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Avattu kasvojen avulla. Jatka lukituksen avauskuvakkeella."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Avattu kasvojen avulla. Avaa painamalla."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Kasvot tunnistettu. Avaa painamalla."</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index eb2081071de9..1fc6b8c9ecd9 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Toucher de nouveau"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Balayez l\'écran vers le haut pour ouvrir"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Appuyez sur l\'icône Déverrouiller pour ouvrir"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Déverr. par reconn. faciale. App. sur l\'icône pour ouvrir."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Déverr. par reconnaissance faciale. Appuyez pour ouvrir."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Visage reconnu. Appuyez pour ouvrir."</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 1c6943f9a207..79acfdbdb882 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Appuyer à nouveau"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Balayer vers le haut pour ouvrir"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Appuyez sur l\'icône de déverrouillage pour ouvrir"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Déverrouillé par visage. Appuyez sur icône déverrouillage pour ouvrir."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Déverrouillé par visage. Appuyez pour ouvrir."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Visage reconnu. Appuyez pour ouvrir."</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 9258be9a5d8c..e73aa032171a 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Toca de novo"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Pasa o dedo cara arriba para abrir"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Preme a icona de desbloquear para abrir a porta"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Usouse o desbloqueo facial. Preme a icona de desbloquear."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Usouse o desbloqueo facial. Preme para abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Recoñeceuse a cara. Preme para abrir."</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index f3cc2dd7eaff..e00f5ff22297 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"ફરીથી ટૅપ કરો"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"ખોલવા માટે ઉપરની તરફ સ્વાઇપ કરો"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"ખોલવા માટે \'અનલૉક કરો\' આઇકન દબાવો"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ચહેરા દ્વારા અનલૉક કર્યું. ખોલવા \'અનલૉક કરો\' આઇકન દબાવો."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ચહેરા દ્વારા અનલૉક કર્યું. ખોલવા માટે દબાવો."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ચહેરો ઓળખ્યો. ખોલવા માટે દબાવો."</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 15612080da5c..b74d2874fcad 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"फिर से टैप करें"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"खोलने के लिए ऊपर स्वाइप करें"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"डिवाइस अनलॉक करने के लिए, अनलॉक आइकॉन को दबाएं"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"चेहरे से अनलॉक किया. डिवाइस अनलॉक करने के लिए, अनलॉक आइकॉन को दबाएं."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"चेहरे से अनलॉक किया गया. डिवाइस खोलने के लिए टैप करें."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"चेहरे की पहचान हो गई. डिवाइस खोलने के लिए टैप करें."</string> @@ -950,10 +952,7 @@ <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> - <!-- no translation found for dream_date_complication_date_format (8191225366513860104) --> - <skip /> - <!-- no translation found for dream_time_complication_12_hr_time_format (4691197486690291529) --> - <skip /> - <!-- no translation found for dream_time_complication_24_hr_time_format (6248280719733640813) --> - <skip /> + <string name="dream_date_complication_date_format" msgid="8191225366513860104">"EEE, MMM d"</string> + <string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string> + <string name="dream_time_complication_24_hr_time_format" msgid="6248280719733640813">"kk:mm"</string> </resources> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 01ad02b69356..823d2f495d2a 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Dodirnite ponovo"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Prijeđite prstom prema gore da biste otvorili"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Pritisnite ikonu otključavanja da biste otvorili"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Otključano pomoću lica. Pritisnite ikonu otključavanja da biste otvorili."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Otključano pomoću lica. Pritisnite da biste otvorili."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Lice je prepoznato. Pritisnite da biste otvorili."</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 8d33bc8f4c56..12e2d70de609 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Koppintson újra"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Csúsztasson felfelé a megnyitáshoz"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Az eszköz használatához nyomja meg a feloldás ikonját"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Arccal feloldva. A megnyitáshoz nyomja meg a feloldás ikont."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Zárolás arccal feloldva. Koppintson az eszköz használatához."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Arc felismerve. Koppintson az eszköz használatához."</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index c69ee42cd85f..c702969c9058 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Նորից հպեք"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Բացելու համար սահեցրեք վերև"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Բացեք՝ սեղմելով ապակողպման պատկերակը"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Ապակողպվել է դեմքով։ Բացեք՝ սեղմելով ապակողպման պատկերակը։"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Ապակողպվել է դեմքով։ Սեղմեք բացելու համար։"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Դեմքը ճանաչվեց։ Սեղմեք բացելու համար։"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 19b9d40e84fe..e52d08d7f721 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Ketuk lagi"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Geser ke atas untuk membuka"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Tekan ikon buka kunci untuk membuka"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Kunci dibuka dengan wajah. Tekan ikon buka kunci untuk membuka."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Kunci dibuka dengan wajah. Tekan untuk membuka."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Wajah dikenali. Tekan untuk membuka."</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 93ed60375bbd..e8d2d866aec3 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Ýttu aftur"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Strjúktu upp til að opna"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Ýttu á táknið til að taka úr lás til að opna"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Opnað með andliti. Ýttu á táknið taka úr lás til að opna."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Opnað með andliti. Ýttu til að opna."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Andlitið var greint. Ýttu til að opna."</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 02917338874e..5d318bfb1adf 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Tocca di nuovo"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Scorri verso l\'alto per aprire"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Premi l\'icona Sblocca per aprire"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Sbloccato con il volto. Premi l\'icona Sblocca per aprire."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Sbloccato con il volto. Premi per aprire."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Volto riconosciuto. Premi per aprire."</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 6a0199c66e3c..25e08c634b6b 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"צריך להקיש פעם נוספת"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"צריך להחליק כדי לפתוח"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"לפתיחה, לוחצים על סמל ביטול הנעילה"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"הנעילה בוטלה בזיהוי פנים. פותחים בלחיצה על סמל ביטול הנעילה."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"הנעילה בוטלה באמצעות זיהוי הפנים. יש ללחוץ כדי לפתוח."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"הפנים זוהו. יש ללחוץ כדי לפתוח."</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 3a34f3c2206e..69a959268d7f 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"もう一度タップしてください"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"開くには上にスワイプします"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"ロック解除アイコンを押して開きます"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"顔でロック解除しました。アイコンを押すと開きます。"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"顔でロック解除しました。押すと開きます。"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"顔を認識しました。押すと開きます。"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 57b56f7b57e7..fc1ecdac36ee 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"შეეხეთ ხელახლა"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"გასახსნელად გადაფურცლეთ ზემოთ"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"გასახსნელად დააჭირეთ განბლოკვის ხატულას"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"განიბლოკა სახით. გასახსნელად დააჭირეთ განბლოკვის ხატულას."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"განიბლოკა სახით. დააჭირეთ გასახსნელად."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ამოცნობილია სახით. დააჭირეთ გასახსნელად."</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index e14c8274d125..d47c26df09a8 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Қайта түртіңіз."</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Ашу үшін жоғары қарай сырғытыңыз."</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Ашу үшін құлыпты ашу белгішесін басыңыз."</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Бет үлгісі арқылы ашылды. Ашу үшін құлыпты ашу белгішесін басыңыз."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Бетпен ашылды. Ашу үшін басыңыз."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Бет танылды. Ашу үшін басыңыз."</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 14fa8de52289..48fedd222a1f 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"ចុចម្ដងទៀត"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"អូសឡើងលើដើម្បីបើក"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"ចុចរូបដោះសោ ដើម្បីបើក"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"បានដោះសោដោយប្រើមុខ។ សូមចុចរូបដោះសោ ដើម្បីបើក។"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"បានដោះសោដោយប្រើមុខ។ សូមចុច ដើម្បីបើក។"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"បានស្គាល់មុខ។ សូមចុច ដើម្បីបើក។"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index f980937cca84..55afb910c775 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"ಪುನಃ ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"ತೆರೆಯಲು ಮೇಲಕ್ಕೆ ಸ್ವೈಪ್ ಮಾಡಿ"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"ತೆರೆಯಲು ಅನ್ಲಾಕ್ ಐಕಾನ್ ಅನ್ನು ಒತ್ತಿ"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ಮುಖವನ್ನು ಬಳಸಿ ಅನ್ಲಾಕ್ ಮಾಡಲಾಗಿದೆ. ತೆರೆಯಲು ಅನ್ಲಾಕ್ ಐಕಾನ್ ಅನ್ನು ಒತ್ತಿ."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ಮುಖವನ್ನು ಬಳಸಿ ಅನ್ಲಾಕ್ ಮಾಡಲಾಗಿದೆ. ತೆರೆಯಲು ಒತ್ತಿ."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ಮುಖ ಗುರುತಿಸಲಾಗಿದೆ. ತೆರೆಯಲು ಒತ್ತಿ."</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index cd88795f771b..32d345f04532 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"다시 탭하세요."</string> <string name="keyguard_unlock" msgid="8031975796351361601">"위로 스와이프하여 열기"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"기기를 열려면 잠금 해제 아이콘을 누르세요."</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"얼굴 인식으로 잠금 해제되었습니다. 기기를 열려면 잠금 해제 아이콘을 누르세요."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"얼굴 인식으로 잠금 해제되었습니다. 열려면 누르세요."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"얼굴이 인식되었습니다. 열려면 누르세요."</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 391311159d34..a9670589f0cc 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Кайра таптап коюңуз"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Ачуу үчүн өйдө сүрүңүз"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Кулпуну ачуу сүрөтчөсүн басыңыз"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Кулпуну жүзүңүз менен ачтыңыз. Эми кулпуну ачуу сүрөтчөсүн басыңыз."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Кулпуну жүзүңүз менен ачтыңыз. Ачуу үчүн басыңыз."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Жүз таанылды. Ачуу үчүн басыңыз."</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 469f77bf60d0..d86198e4d512 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"ແຕະອີກເທື່ອໜຶ່ງ"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"ປັດຂຶ້ນເພື່ອເປີດ"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"ກົດໄອຄອນປົດລັອກເພື່ອເປີດ"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ປົດລັອກດ້ວຍໜ້າແລ້ວ. ກົດໄອຄອນປົດລັອກເພື່ອເປີດ."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ປົດລັອກດ້ວຍໜ້າແລ້ວ. ກົດເພື່ອເປີດ."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ຈຳແນກໜ້າໄດ້ແລ້ວ. ກົດເພື່ອເປີດ."</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 66c9c3466f6a..56bfb2c3713c 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Palieskite dar kartą"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Perbraukite aukštyn, kad atidarytumėte"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Paspauskite atrakinimo piktogramą, kad atidarytumėte"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Atrakinta pagal veidą. Pasp. atr. pikt., kad atidarytumėte."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Atrakinta pagal veidą. Paspauskite, kad atidarytumėte."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Veidas atpažintas. Paspauskite, kad atidarytumėte."</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index a32c5e41be30..65e7c5d74b39 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Pieskarieties vēlreiz"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Velciet augšup, lai atvērtu"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Lai atvērtu, nospiediet atbloķēšanas ikonu"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Atbloķēta ar seju. Atvērt: nospiediet atbloķēšanas ikonu."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Ierīce atbloķēta ar seju. Nospiediet, lai atvērtu."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Seja atpazīta. Nospiediet, lai atvērtu."</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index d7951b190241..85f9cd5329d0 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Допрете повторно"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Повлечете за да отворите"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Притиснете ја иконата за отклучување за да отворите"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Отклучено со лик. Притиснете ја иконата за отклучување за да отворите."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Отклучено со лик. Притиснете за да отворите."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Лицето е препознаено. Притиснете за да отворите."</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 5b0164d8e3be..49a807545283 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"വീണ്ടും ടാപ്പ് ചെയ്യുക"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"തുറക്കാൻ മുകളിലോട്ട് സ്വൈപ്പ് ചെയ്യുക"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"തുറക്കാൻ അൺലോക്ക് ഐക്കൺ അമർത്തുക"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"മുഖം ഉപയോഗിച്ച് അൺലോക്ക് ചെയ്തു. തുറക്കാൻ അൺലോക്ക് ഐക്കൺ അമർത്തുക."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"മുഖം ഉപയോഗിച്ച് അൺലോക്ക് ചെയ്തു. തുറക്കാൻ അമർത്തുക."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"മുഖം തിരിച്ചറിഞ്ഞു. തുറക്കാൻ അമർത്തുക."</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 2648c5c2477d..c9bd0bf20243 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Дaхин товшино уу"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Нээхийн тулд дээш шударна уу"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Нээхийн тулд түгжээг тайлах дүрс тэмдэг дээр дараарай"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Царайгаар түгжээг тайлсан. Нээхийн тулд түгжээг тайлах дүрс тэмдэг дээр дараарай."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Царайгаар түгжээг тайлсан. Нээхийн тулд дарна уу."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Царайг таньсан. Нээхийн тулд дарна уу."</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 7129762b6e58..5922126dcebe 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"पुन्हा टॅप करा"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"उघडण्यासाठी वर स्वाइप करा"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"उघडण्यासाठी अनलॉक करा आयकन दाबा"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"चेहऱ्याने अनलॉक केले. उघडण्यासाठी अनलॉक करा आयकन दाबा."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"चेहऱ्याने अनलॉक केले आहे. उघडण्यासाठी दाबा."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"चेहरा ओळखला आहे. उघडण्यासाठी दाबा."</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index e37d88cd4f8a..ac6f3deb8e53 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Ketik sekali lagi"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Leret ke atas untuk buka"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Tekan ikon buka kunci untuk buka"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Dibuka kunci dengan wajah. Tekan ikon buka kunci untuk buka."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Dibuka kunci dengan wajah. Tekan untuk membuka."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Wajah dicam. Tekan untuk membuka."</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 301d42e2e3c3..09de7451064f 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"ထပ်တို့ပါ"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"ဖွင့်ရန် အပေါ်သို့ပွတ်ဆွဲပါ"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"ဖွင့်ရန် လော့ခ်ဖွင့်သင်္ကေတကို နှိပ်ပါ"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"မျက်နှာပြ လော့ခ်ဖွင့်ထားသည်။ လော့ခ်ဖွင့်သင်္ကေတ နှိပ်၍ဝင်ပါ။"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"မျက်နှာဖြင့် ဖွင့်ထားသည်။ ဖွင့်ရန် နှိပ်ပါ။"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"မျက်နှာ မှတ်မိသည်။ ဖွင့်ရန် နှိပ်ပါ။"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 64bc528fc71f..8b435c2fd43d 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -133,7 +133,7 @@ <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Ansiktet er autentisert"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Bekreftet"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Trykk på Bekreft for å fullføre"</string> - <string name="biometric_dialog_tap_confirm_with_face" msgid="1092050545851021991">"Låst opp med ansiktet. Trykk på lås opp-ikon for å fortsette"</string> + <string name="biometric_dialog_tap_confirm_with_face" msgid="1092050545851021991">"Låst opp med ansiktet. Trykk på lås opp-ikonet for å fortsette"</string> <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"Låst opp med ansiktet. Trykk for å fortsette."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Ansiktet er gjenkjent. Trykk for å fortsette."</string> <string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Ansiktet er gjenkjent. Trykk på lås opp-ikon for å fortsette"</string> @@ -307,7 +307,9 @@ <string name="tap_again" msgid="1315420114387908655">"Trykk igjen"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Sveip opp for å åpne"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Trykk på lås opp-ikonet for å åpne"</string> - <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Låst opp med ansiktet. Trykk på lås opp-ikon for å fortsette"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> + <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Låst opp med ansiktet. Trykk på lås opp-ikonet for å fortsette"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Låst opp med ansiktet. Trykk for å åpne."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Ansiktet er gjenkjent. Trykk for å åpne."</string> <string name="keyguard_face_successful_unlock_press_alt_3" msgid="7219030481255573962">"Ansiktet er gjenkjent. Trykk på lås opp-ikon for å fortsette"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 4db5278a170d..05bb2868125c 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"फेरि ट्याप गर्नुहोस्"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"खोल्न माथितिर स्वाइप गर्नुहोस्"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"खोल्न अनलक आइकनमा थिच्नुहोस्"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"अनुहार प्रयोग गरी अनलक गरियो। खोल्न अनलक आइकनमा थिच्नुहोस्।"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"अनुहार प्रयोग गरी अनलक गरियो। डिभाइस खोल्न थिच्नुहोस्।"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"अनुहार पहिचान गरियो। डिभाइस खोल्न थिच्नुहोस्।"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 07c53c513c73..2c91600cc5e5 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Tik nog een keer"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Swipe omhoog om te openen"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Druk op het ontgrendelicoon om te openen"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Ontgrendeld via gezicht. Druk op het ontgrendelicoon."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Ontgrendeld via gezicht. Druk om te openen."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Gezicht herkend. Druk om te openen."</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 7eda9bc84dd3..f30f7d13d0f7 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -79,8 +79,8 @@ <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"ସ୍କ୍ରିନସଟକୁ ସେଭ୍ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ଆପ୍ କିମ୍ବା ସଂସ୍ଥା ଦ୍ୱାରା ସ୍କ୍ରୀନଶଟ୍ ନେବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string> <string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"ସ୍କ୍ରିନସଟଗୁଡ଼ିକ ନେବା ଆପଣଙ୍କ IT ଆଡମିନଙ୍କ ଦ୍ୱାରା ବ୍ଲକ କରାଯାଇଛି"</string> - <string name="screenshot_edit_label" msgid="8754981973544133050">"ଏଡିଟ୍ କରନ୍ତୁ"</string> - <string name="screenshot_edit_description" msgid="3333092254706788906">"ସ୍କ୍ରିନସଟ୍ ଏଡିଟ୍ କରନ୍ତୁ"</string> + <string name="screenshot_edit_label" msgid="8754981973544133050">"ଏଡିଟ କରନ୍ତୁ"</string> + <string name="screenshot_edit_description" msgid="3333092254706788906">"ସ୍କ୍ରିନସଟ୍ ଏଡିଟ କରନ୍ତୁ"</string> <string name="screenshot_share_description" msgid="2861628935812656612">"ସ୍କ୍ରିନସଟ ସେୟାର କରନ୍ତୁ"</string> <string name="screenshot_scroll_label" msgid="2930198809899329367">"ଅଧିକ କ୍ୟାପଚର୍ କରନ୍ତୁ"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ସ୍କ୍ରିନସଟ୍ ଖାରଜ କରନ୍ତୁ"</string> @@ -124,10 +124,10 @@ <string name="accessibility_lock_icon" msgid="661492842417875775">"ଡିଭାଇସ୍ ଲକ୍ ହୋଇଯାଇଛି"</string> <string name="accessibility_scanning_face" msgid="3093828357921541387">"ଫେସ୍ ସ୍କାନିଙ୍ଗ କରାଯାଉଛି"</string> <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"ପଠାନ୍ତୁ"</string> - <string name="cancel" msgid="1089011503403416730">"ବାତିଲ୍ କରନ୍ତୁ"</string> + <string name="cancel" msgid="1089011503403416730">"ବାତିଲ କରନ୍ତୁ"</string> <string name="biometric_dialog_confirm" msgid="2005978443007344895">"ନିଶ୍ଚିତ କରନ୍ତୁ"</string> <string name="biometric_dialog_try_again" msgid="8575345628117768844">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string> - <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ପ୍ରାମାଣିକତା ବାତିଲ୍ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string> + <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"ପ୍ରାମାଣିକତା ବାତିଲ କରିବାକୁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string> <string name="biometric_dialog_face_icon_description_authenticating" msgid="3401633342366146535">"ଆପଣଙ୍କର ମୁହଁକୁ ପ୍ରମାଣ କରୁଛି"</string> <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"ମୁହଁ ପ୍ରାମାଣିକତା ହୋଇଛି"</string> @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"ପୁଣି ଟାପ୍ କରନ୍ତୁ"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"ଖୋଲିବା ପାଇଁ ଉପରକୁ ସ୍ୱାଇପ୍ କରନ୍ତୁ"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"ଖୋଲିବାକୁ ଅନଲକ ଆଇକନ ଦବାନ୍ତୁ"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ଫେସ ମାଧ୍ୟମରେ ଅନଲକ କରାଯାଇଛି। ଖୋଲିବାକୁ ଅନଲକ ଆଇକନ ଦବାନ୍ତୁ।"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ଫେସ ମାଧ୍ୟମରେ ଅନଲକ କରାଯାଇଛି। ଖୋଲିବାକୁ ଦବାନ୍ତୁ।"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ଫେସ ଚିହ୍ନଟ କରାଯାଇଛି। ଖୋଲିବାକୁ ଦବାନ୍ତୁ।"</string> @@ -619,7 +621,7 @@ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"ଟାଇଲ୍ ପୁଣି ସଜାଇବାକୁ ଦାବିଧରି ଟାଣନ୍ତୁ"</string> <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"ଆପଣଙ୍କର ଅତିକମ୍ରେ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>ଟି ଟାଇଲ୍ ଆବଶ୍ୟକ"</string> - <string name="qs_edit" msgid="5583565172803472437">"ଏଡିଟ୍ କରନ୍ତୁ"</string> + <string name="qs_edit" msgid="5583565172803472437">"ଏଡିଟ କରନ୍ତୁ"</string> <string name="tuner_time" msgid="2450785840990529997">"ସମୟ"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"ଘଣ୍ଟା, ମିନିଟ୍ ଏବଂ ସେକେଣ୍ଡ ଦେଖାନ୍ତୁ"</item> @@ -651,7 +653,7 @@ <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ଉପଯୋଗକର୍ତ୍ତା ବାଛନ୍ତୁ"</string> <string name="data_connection_no_internet" msgid="691058178914184544">"କୌଣସି ଇଣ୍ଟରନେଟ୍ କନେକ୍ସନ୍ ନାହିଁ"</string> <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> ସେଟିଙ୍ଗ ଖୋଲନ୍ତୁ।"</string> - <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ସେଟିଙ୍ଗର କ୍ରମ ସଂଶୋଧନ କରନ୍ତୁ।"</string> + <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"ସେଟିଂସର କ୍ରମ ଏଡିଟ କରନ୍ତୁ।"</string> <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ପାୱାର ମେନୁ"</string> <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ପୃଷ୍ଠା <xliff:g id="ID_1">%1$d</xliff:g> ମୋଟ <xliff:g id="ID_2">%2$d</xliff:g>"</string> <string name="tuner_lock_screen" msgid="2267383813241144544">"ଲକ୍ ସ୍କ୍ରୀନ୍"</string> @@ -823,7 +825,7 @@ <string name="controls_error_generic" msgid="352500456918362905">"ସ୍ଥିତି ଲୋଡ୍ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="controls_error_failed" msgid="960228639198558525">"ତ୍ରୁଟି ହୋଇଛି, ପୁଣି ଚେଷ୍ଟା କର"</string> <string name="controls_menu_add" msgid="4447246119229920050">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକ ଯୋଗ କରନ୍ତୁ"</string> - <string name="controls_menu_edit" msgid="890623986951347062">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ଏଡିଟ୍ କରନ୍ତୁ"</string> + <string name="controls_menu_edit" msgid="890623986951347062">"ନିୟନ୍ତ୍ରଣଗୁଡ଼ିକୁ ଏଡିଟ କରନ୍ତୁ"</string> <string name="media_output_dialog_add_output" msgid="5642703238877329518">"ଆଉଟପୁଟ୍ ଯୋଗ କରନ୍ତୁ"</string> <string name="media_output_dialog_group" msgid="5571251347877452212">"ଗୋଷ୍ଠୀ"</string> <string name="media_output_dialog_single_device" msgid="3102758980643351058">"1ଟି ଡିଭାଇସ୍ ଚୟନ କରାଯାଇଛି"</string> @@ -935,7 +937,7 @@ <string name="clipboard_editor" msgid="2971197550401892843">"କ୍ଲିପବୋର୍ଡ ଏଡିଟର"</string> <string name="clipboard_overlay_window_name" msgid="6450043652167357664">"କ୍ଲିପବୋର୍ଡ"</string> <string name="clipboard_image_preview" msgid="2156475174343538128">"ଇମେଜ ପ୍ରିଭ୍ୟୁ"</string> - <string name="clipboard_edit" msgid="4500155216174011640">"ଏଡିଟ"</string> + <string name="clipboard_edit" msgid="4500155216174011640">"ଏଡିଟ କରନ୍ତୁ"</string> <string name="add" msgid="81036585205287996">"ଯୋଗ କରନ୍ତୁ"</string> <string name="manage_users" msgid="1823875311934643849">"ଉପଯୋଗକର୍ତ୍ତାମାନଙ୍କୁ ପରିଚାଳନା କରନ୍ତୁ"</string> <string name="drag_split_not_supported" msgid="4326847447699729722">"ଏହି ବିଜ୍ଞପ୍ତି ସ୍ପ୍ଲିଟସ୍କ୍ରିନକୁ ଡ୍ରାଗ କରିବାକୁ ସମର୍ଥନ କରେ ନାହିଁ।"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 425e66472b47..d41e43c3c935 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"ਦੁਬਾਰਾ ਟੈਪ ਕਰੋ"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"ਖੋਲ੍ਹਣ ਲਈ ਉੱਪਰ ਵੱਲ ਸਵਾਈਪ ਕਰੋ"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"ਖੋਲ੍ਹਣ ਲਈ \'ਅਣਲਾਕ ਕਰੋ\' ਪ੍ਰਤੀਕ ਨੂੰ ਦਬਾਓ"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ਚਿਹਰੇ ਰਾਹੀਂ ਅਣਲਾਕ ਕੀਤਾ ਗਿਆ। ਖੋਲ੍ਹਣ ਲਈ \'ਅਣਲਾਕ ਕਰੋ\' ਪ੍ਰਤੀਕ ਨੂੰ ਦਬਾਓ।"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ਚਿਹਰੇ ਰਾਹੀਂ ਅਣਲਾਕ ਕੀਤਾ ਗਿਆ। ਖੋਲ੍ਹਣ ਲਈ ਦਬਾਓ।"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ਚਿਹਰੇ ਦੀ ਪਛਾਣ ਹੋਈ। ਖੋਲ੍ਹਣ ਲਈ ਦਬਾਓ।"</string> @@ -362,7 +364,7 @@ <string name="notification_section_header_gentle" msgid="6804099527336337197">"ਸ਼ਾਂਤ"</string> <string name="notification_section_header_alerting" msgid="5581175033680477651">"ਸੂਚਨਾਵਾਂ"</string> <string name="notification_section_header_conversations" msgid="821834744538345661">"ਗੱਲਾਂਬਾਤਾਂ"</string> - <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ਸਾਰੀਆਂ ਖਾਮੋਸ਼ ਸੂਚਨਾਵਾਂ ਕਲੀਅਰ ਕਰੋ"</string> + <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"ਸਾਰੀਆਂ ਸ਼ਾਂਤ ਸੂਚਨਾਵਾਂ ਕਲੀਅਰ ਕਰੋ"</string> <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"\'ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ\' ਵੱਲੋਂ ਸੂਚਨਾਵਾਂ ਨੂੰ ਰੋਕਿਆ ਗਿਆ"</string> <string name="media_projection_action_text" msgid="3634906766918186440">"ਹੁਣੇ ਸ਼ੁਰੂ ਕਰੋ"</string> <string name="empty_shade_text" msgid="8935967157319717412">"ਕੋਈ ਸੂਚਨਾਵਾਂ ਨਹੀਂ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index ccf51800d2ed..36bbbc4ccbc0 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Kliknij jeszcze raz"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Przesuń w górę, by otworzyć"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Aby otworzyć, kliknij ikonę odblokowywania"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Odblokowano skanem twarzy. Aby otworzyć, kliknij ikonę odblokowywania."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Odblokowano rozpoznawaniem twarzy. Naciśnij, by otworzyć."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Twarz rozpoznana. Naciśnij, by otworzyć."</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 1fa4954ccb58..0cb7420785ba 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Toque novamente"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Deslize para cima para abrir"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Pressione o ícone de desbloqueio para abrir"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Desbloqueado pelo rosto. Toque no ícone do cadeado para abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Desbloqueado pelo rosto. Pressione para abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Rosto reconhecido. Pressione para abrir."</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 7f24b90448f0..9702b9759793 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Toque novamente"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Deslize rapidamente para cima para abrir"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Prima o ícone de desbloqueio para abrir"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Desbloqueio com a face. Prima ícone de desbloqueio p/ abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Desbloqueado com o rosto. Prima para abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Rosto reconhecido. Prima para abrir."</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 1fa4954ccb58..0cb7420785ba 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Toque novamente"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Deslize para cima para abrir"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Pressione o ícone de desbloqueio para abrir"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Desbloqueado pelo rosto. Toque no ícone do cadeado para abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Desbloqueado pelo rosto. Pressione para abrir."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Rosto reconhecido. Pressione para abrir."</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 80cdb953b527..99d2f06b2970 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Atingeți din nou"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Glisați în sus pentru a deschide"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Apăsați pictograma de deblocare pentru a deschide"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"S-a deblocat cu ajutorul feței. Apăsați pictograma de deblocare pentru a deschide"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"S-a deblocat cu ajutorul feței. Apăsați pentru a deschide."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Chipul a fost recunoscut. Apăsați pentru a deschide."</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index dd5e0e5a9561..817fdc0e275d 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Нажмите ещё раз"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Проведите вверх, чтобы открыть"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Нажмите на значок разблокировки."</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Сканирование выполнено. Нажмите на значок разблокировки."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Разблокировано сканированием лица. Нажмите, чтобы открыть."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Лицо распознано. Нажмите, чтобы открыть."</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index b2c393f6c16d..8c7b85cbe287 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"නැවත තට්ටු කරන්න"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"විවෘත කිරීමට ස්වයිප් කරන්න"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"විවෘත කිරීමට අගුලු හැරීමේ නිරූපකය ඔබන්න"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"මුහුණ මගින් අගුලු හරින ලදි. විවෘත කිරීමට අගුලු හැරීමේ නිරූපකය ඔබන්න."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"මුහුණ මගින් අගුලු හරින ලදි. විවෘත කිරීමට ඔබන්න."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"මුහුණ හඳුනා ගන්නා ලදි. විවෘත කිරීමට ඔබන්න."</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 542f197cf429..4bdd20601559 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Klepnite znova"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Otvorte potiahnutím prstom nahor"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Otvorte klepnutím na ikonu odomknutia"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Odomknuté tvárou. Otvorte klepnutím na ikonu odomknutia."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Odomknuté tvárou. Otvorte stlačením."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Tvár bola rozpoznaná. Otvorte stlačením."</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 9dfc7cc3c618..c1afc12ccc54 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Znova se dotaknite možnosti"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Povlecite navzgor, da odprete"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Za odpiranje pritisnite ikono za odklepanje."</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Odklenjeno z obrazom. Za odpiranje pritisnite ikono za odklepanje."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Odklenjeno z obrazom. Pritisnite za odpiranje."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Obraz je prepoznan. Pritisnite za odpiranje."</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 8417ee640566..c3cdf21857c3 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Trokit sërish"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Rrëshqit lart për ta hapur"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Shtyp ikonën e shkyçjes për ta hapur"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"U shkyç me fytyrë. Shtyp ikonën e shkyçjes për ta hapur."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"U shkyç me fytyrë. Shtyp për ta hapur."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Fytyra u njoh. Shtyp për ta hapur."</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 3bb3bfb1124c..ef5993bd9fdd 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Додирните поново"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Превуците нагоре да бисте отворили"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Притисните икону откључавања да бисте отворили."</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Откључано је лицем. Притисните икону откључавања да бисте отворили."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Откључано је лицем. Притисните да бисте отворили."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Лице је препознато. Притисните да бисте отворили."</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index b7f9b05ad4b6..20bc29fc77ce 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Tryck igen"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Öppna genom att svepa uppåt"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Tryck på ikonen lås upp för att öppna"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Upplåst med ansiktslås. Tryck på ikonen lås upp för att öppna."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Upplåst med ansiktslås. Tryck för att öppna."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Ansiktet har identifierats. Tryck för att öppna."</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 1f5b7eac04c1..418b88eede96 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Gusa tena"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Telezesha kidole juu ili ufungue"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Bonyeza aikoni ya kufungua ili ufungue"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Imefunguliwa kwa kutumia uso wako. Bonyeza aikoni ya kufungua ili ufungue."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Imefunguliwa kwa kutumia uso wako. Bonyeza ili ufungue."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Uso umetambuliwa. Bonyeza ili ufungue."</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 8fea33e79de8..34cb0cf9de5b 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"மீண்டும் தட்டவும்"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"திறப்பதற்கு மேல் நோக்கி ஸ்வைப் செய்யவும்"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"திறக்க, அன்லாக் ஐகானை அழுத்தவும்"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"முகம் மூலம் அன்லாக் செய்யப்பட்டது. திறக்க, அன்லாக் ஐகானை அழுத்துக."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"முகம் மூலம் அன்லாக் செய்யப்பட்டது. திறக்க அழுத்தவும்."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"முகம் அங்கீகரிக்கப்பட்டது. திறக்க அழுத்தவும்."</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 759ebb471bb5..720c909b5223 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -50,7 +50,7 @@ <string name="usb_debugging_title" msgid="8274884945238642726">"USB డీబగ్గింగ్ను అనుమతించాలా?"</string> <string name="usb_debugging_message" msgid="5794616114463921773">"ఇది కంప్యూటర్ యొక్క RSA కీ వేలిముద్ర:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string> <string name="usb_debugging_always" msgid="4003121804294739548">"ఈ కంప్యూటర్ నుండి ఎల్లప్పుడూ అనుమతించు"</string> - <string name="usb_debugging_allow" msgid="1722643858015321328">"అనుమతించు"</string> + <string name="usb_debugging_allow" msgid="1722643858015321328">"అనుమతించండి"</string> <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"USB డీబగ్గింగ్కి అనుమతి లేదు"</string> <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"ఈ పరికరానికి ప్రస్తుతం సైన్ ఇన్ చేసిన వినియోగదారు USB డీబగ్గింగ్ ఆన్ చేయలేరు. ఈ ఫీచర్ ఉపయోగించడానికి, ప్రాథమిక వినియోగదారుకి మారాలి."</string> <string name="hdmi_cec_set_menu_language_title" msgid="1259765420091503742">"మీరు సిస్టమ్ భాషను <xliff:g id="LANGUAGE">%1$s</xliff:g> భాషకు మార్చాలనుకుంటున్నారా?"</string> @@ -60,7 +60,7 @@ <string name="wifi_debugging_title" msgid="7300007687492186076">"ఈ నెట్వర్క్ ద్వారా వైర్లెస్ డీబగ్గింగ్ను అనుమతించాలా?"</string> <string name="wifi_debugging_message" msgid="5461204211731802995">"నెట్వర్క్ పేరు (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nWi‑Fi అడ్రస్ (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string> <string name="wifi_debugging_always" msgid="2968383799517975155">"ఈ నెట్వర్క్ నుండి ఎల్లప్పుడూ అనుమతించు"</string> - <string name="wifi_debugging_allow" msgid="4573224609684957886">"అనుమతించు"</string> + <string name="wifi_debugging_allow" msgid="4573224609684957886">"అనుమతించండి"</string> <string name="wifi_debugging_secondary_user_title" msgid="2493201475880517725">"వైర్లెస్ డీబగ్గింగ్కి అనుమతి లేదు"</string> <string name="wifi_debugging_secondary_user_message" msgid="4492383073970079751">"ఈ పరికరానికి ప్రస్తుతం సైన్ ఇన్ చేసిన యూజర్, వైర్లెస్ డీబగ్గింగ్ ఆన్ చేయలేరు. ఈ ఫీచర్ ఉపయోగించడానికి, ప్రాథమిక యూజర్ కి స్విచ్ అవ్వండి."</string> <string name="usb_contaminant_title" msgid="894052515034594113">"USB పోర్ట్ నిలిపివేయబడింది"</string> @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"మళ్లీ ట్యాప్ చేయండి"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"తెరవడానికి, పైకి స్వైప్ చేయండి"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"తెరవడానికి అన్లాక్ చిహ్నాన్ని నొక్కండి"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ముఖం ద్వారా అన్లాక్ చేయబడింది. తెరవడానికి అన్లాక్ చిహ్నాన్ని నొక్కండి."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ముఖం ద్వారా అన్లాక్ చేయబడింది. తెరవడానికి నొక్కండి."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"ముఖం గుర్తించబడింది. తెరవడానికి నొక్కండి."</string> @@ -712,7 +714,7 @@ <string name="slice_permission_text_1" msgid="6675965177075443714">"- ఇది <xliff:g id="APP">%1$s</xliff:g> నుండి సమాచారాన్ని చదువుతుంది"</string> <string name="slice_permission_text_2" msgid="6758906940360746983">"- ఇది <xliff:g id="APP">%1$s</xliff:g> లోపల చర్యలు తీసుకుంటుంది"</string> <string name="slice_permission_checkbox" msgid="4242888137592298523">"ఏ యాప్ నుండి అయినా స్లైస్లను చూపించడానికి <xliff:g id="APP">%1$s</xliff:g>ని అనుమతించండి"</string> - <string name="slice_permission_allow" msgid="6340449521277951123">"అనుమతించు"</string> + <string name="slice_permission_allow" msgid="6340449521277951123">"అనుమతించండి"</string> <string name="slice_permission_deny" msgid="6870256451658176895">"తిరస్కరించు"</string> <string name="auto_saver_title" msgid="6873691178754086596">"బ్యాటరీ సేవర్ని షెడ్యూల్ చేయడానికి నొక్కండి"</string> <string name="auto_saver_text" msgid="3214960308353838764">"బ్యాటరీ ఛార్జింగ్ పూర్తిగా అయిపోతున్న తరుణంలో ఆన్ చేస్తుంది"</string> @@ -818,7 +820,7 @@ <string name="controls_error_timeout" msgid="794197289772728958">"ఇన్యాక్టివ్, యాప్ చెక్ చేయండి"</string> <string name="controls_error_removed" msgid="6675638069846014366">"కనుగొనబడలేదు"</string> <string name="controls_error_removed_title" msgid="1207794911208047818">"కంట్రోల్ అందుబాటులో లేదు"</string> - <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g>ను యాక్సెస్ చేయడం సాధ్యపడలేదు. <xliff:g id="APPLICATION">%2$s</xliff:g> యాప్ను తనిఖీ చేసి, కంట్రోల్ ఇప్పటికీ అందుబాటులో ఉందని, యాప్ సెట్టింగ్లు మారలేదని నిర్ధారించుకోండి."</string> + <string name="controls_error_removed_message" msgid="2885911717034750542">"<xliff:g id="DEVICE">%1$s</xliff:g>ను యాక్సెస్ చేయడం సాధ్యపడలేదు. <xliff:g id="APPLICATION">%2$s</xliff:g> యాప్ను చెక్ చేసి, కంట్రోల్ ఇప్పటికీ అందుబాటులో ఉందని, యాప్ సెట్టింగ్లు మారలేదని నిర్ధారించుకోండి."</string> <string name="controls_open_app" msgid="483650971094300141">"యాప్ను తెరువు"</string> <string name="controls_error_generic" msgid="352500456918362905">"స్టేటస్ లోడ్ చేయడం సాధ్యపడలేదు"</string> <string name="controls_error_failed" msgid="960228639198558525">"ఎర్రర్, మళ్లీ ప్రయత్నించండి"</string> diff --git a/packages/SystemUI/res/values-television/config.xml b/packages/SystemUI/res/values-television/config.xml index 94f6c3952e06..375bd394bb4c 100644 --- a/packages/SystemUI/res/values-television/config.xml +++ b/packages/SystemUI/res/values-television/config.xml @@ -22,7 +22,7 @@ <resources> <!-- SystemUIFactory component --> <string name="config_systemUIFactoryComponent" translatable="false"> - com.android.systemui.tv.TvSystemUIFactory + com.android.systemui.tv.TvSystemUIInitializer </string> <!-- Svelte specific logic, see RecentsConfiguration.SVELTE_* constants. --> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 99c2e32a6947..8958be0ea95a 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"แตะอีกครั้ง"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"เลื่อนขึ้นเพื่อเปิด"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"กดไอคอนปลดล็อกเพื่อเปิด"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"ปลดล็อกด้วยใบหน้าแล้ว กดไอคอนปลดล็อกเพื่อเปิด"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"ปลดล็อกด้วยใบหน้าแล้ว กดเพื่อเปิด"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"จดจำใบหน้าได้ กดเพื่อเปิด"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 2943313eb071..134005c85545 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"I-tap ulit"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Mag-swipe pataas para buksan"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Pindutin ang icon ng unlock para buksan"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Na-unlock gamit ang mukha. Pindutin ang icon ng unlock para buksan."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Na-unlock gamit ang mukha. Pindutin para buksan."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Nakilala ang mukha. Pindutin para buksan."</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 00569a0b5f5c..fb928b2f5c4a 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Tekrar dokunun"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Açmak için yukarı kaydırın"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Açmak için Kilit açma simgesine basın"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Kilit, yüzünüzle açıldı. Kilit açma simgesine basın."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Cihazın kilidini yüzünüzle açtınız. Açmak için basın."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Yüzünüz tanındı. Açmak için basın."</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 85573a925293..3df26f662787 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Натисніть знову"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Проведіть пальцем угору, щоб відкрити"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Щоб відкрити, натисніть значок розблокування."</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Розблоковано (фейсконтроль). Натисніть значок розблокування."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Розблоковано (фейсконтроль). Натисніть, щоб відкрити."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Обличчя розпізнано. Натисніть, щоб відкрити."</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index d9d1cbd12fb3..40e0bb3e3148 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"دوبارہ تھپتھپائیں"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"کھولنے کے لیے اوپر سوائپ کريں"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"کھولنے کیلئے انلاک آئیکن دبائیں"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"چہرے سے انلاک کیا گیا۔ کھولنے کیلئے انلاک آئیکن دبائیں۔"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"چہرے سے انلاک کیا گیا۔ کھولنے کے لیے دبائیں۔"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"چہرے کی شناخت ہو گئی۔ کھولنے کے لیے دبائیں۔"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 65a3e5113902..a6490f429742 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Yana bosing"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Ochish uchun tepaga suring"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Ochish uchun ochish belgisini bosing"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Yuz orqali ochilgan. Ochish uchun ochish belgisini bosing."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Yuz orqali ochildi. Ochish uchun bosing."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Yuz aniqlandi. Ochish uchun bosing."</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 7340db2fe58d..3a579f15ead4 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Nhấn lại"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Vuốt lên để mở"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Nhấn biểu tượng mở khoá để mở"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Đã mở khoá bằng khuôn mặt. Nhấn vào biểu tượng mở khoá để mở."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Đã mở khoá bằng khuôn mặt. Nhấn để mở."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Đã nhận diện khuôn mặt. Nhấn để mở."</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 96d895832bfe..b46406e9d31e 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"请再点按一次"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"向上滑动即可打开"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"按下解锁图标即可打开"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"已通过面孔识别解锁。按下解锁图标即可打开。"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"已通过面孔识别解锁。点按即可打开。"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"识别出面孔。点按即可打开。"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 89c7c29e4494..1958d0df7401 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"再次輕按"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"按解鎖圖示即可開啟"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"已使用面孔解鎖。按解鎖圖示即可開啟。"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"已使用面孔解鎖。按下即可開啟。"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"已識別面孔。按下即可開啟。"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 9e22840d6478..22d0544f3314 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"再輕觸一次"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"向上滑動即可開啟"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"按下「解鎖」圖示即可開啟"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"裝置已透過人臉解鎖,按下「解鎖」圖示即可開啟。"</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"裝置已透過你的臉解鎖,按下即可開啟。"</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"臉孔辨識完成,按下即可開啟。"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index a6e302664728..4000ce00de59 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -307,6 +307,8 @@ <string name="tap_again" msgid="1315420114387908655">"Thepha futhi"</string> <string name="keyguard_unlock" msgid="8031975796351361601">"Swayiphela phezulu ukuze uvule"</string> <string name="keyguard_unlock_press" msgid="9140109453735019209">"Cindezela isithonjana sokuvula ukuze uvule"</string> + <!-- no translation found for keyguard_face_successful_unlock_swipe (6180997591385846073) --> + <skip /> <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"Ivulwe ngobuso. Cindezela isithonjana sokuvula ukuze uvule."</string> <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"Vula ngobuso. Cindezela ukuze uvule."</string> <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Ubuso buyaziwa. Cindezela ukuze uvule."</string> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index ccf18d2a0bb1..1eece4cee179 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -129,7 +129,6 @@ <color name="smart_reply_button_stroke">@*android:color/accent_device_default</color> <!-- Biometric dialog colors --> - <color name="biometric_dialog_dim_color">#80000000</color> <!-- 50% black --> <color name="biometric_dialog_gray">#ff757575</color> <color name="biometric_dialog_accent">@color/material_dynamic_primary40</color> <color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 --> diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 771973c36053..82a3b58a5155 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -286,7 +286,7 @@ <bool name="config_enableFullscreenUserSwitcher">false</bool> <!-- SystemUIFactory component --> - <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIFactory</string> + <string name="config_systemUIFactoryComponent" translatable="false">com.android.systemui.SystemUIInitializerImpl</string> <!-- QS tile shape store width. negative implies fill configuration instead of stroke--> <dimen name="config_qsTileStrokeWidthActive">-1dp</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index bb5c5928181a..022a6b2f4da2 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1449,6 +1449,7 @@ @*android:dimen/status_bar_system_icon_size</dimen> <dimen name="dream_overlay_camera_mic_off_indicator_size">8dp</dimen> <dimen name="dream_overlay_notification_indicator_size">6dp</dimen> + <dimen name="dream_overlay_grey_chip_width">56dp</dimen> <!-- Dream overlay complications related dimensions --> <dimen name="dream_overlay_complication_clock_time_text_size">100sp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index ef672f3a6213..9c2542cbd05f 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -802,6 +802,8 @@ <!-- Message shown when lock screen is unlocked (ie: by trust agent) and the user taps the empty space on the lock screen and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] --> <string name="keyguard_unlock_press">Press the unlock icon to open</string> + <!-- Message shown when non-bypass face authentication succeeds. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] --> + <string name="keyguard_face_successful_unlock_swipe">Unlocked by face. Swipe up to open.</string> <!-- Message shown when non-bypass face authentication succeeds and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] --> <string name="keyguard_face_successful_unlock_press">Unlocked by face. Press the unlock icon to open.</string> <!-- Message shown when non-bypass face authentication succeeds and UDFPS is supported. Provides extra instructions for how the user can enter their device [CHAR LIMIT=60] --> @@ -2295,8 +2297,8 @@ <string name="media_output_dialog_disconnected">(disconnected)</string> <!-- Summary for connecting error message [CHAR LIMIT=NONE] --> <string name="media_output_dialog_connect_failed">Can\'t switch. Tap to try again.</string> - <!-- Title for pairing item [CHAR LIMIT=60] --> - <string name="media_output_dialog_pairing_new">Pair new device</string> + <!-- Title for connecting item [CHAR LIMIT=60] --> + <string name="media_output_dialog_pairing_new">Connect a device</string> <!-- Title for launch app [CHAR LIMIT=60] --> <string name="media_output_dialog_launch_app_text">To cast this session, please open the app.</string> <!-- App name when can't get app name [CHAR LIMIT=60] --> @@ -2553,6 +2555,10 @@ <string name="dream_overlay_status_bar_priority_mode">Priority mode</string> <!-- Content description for the alarm set icon in the dream overlay status bar [CHAR LIMIT=NONE] --> <string name="dream_overlay_status_bar_alarm_set">Alarm set</string> + <!-- Content description for the camera off icon in the dream overlay status bar [CHAR LIMIT=NONE] --> + <string name="dream_overlay_status_bar_camera_off">Camera is off</string> + <!-- Content description for the mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] --> + <string name="dream_overlay_status_bar_mic_off">Mic is off</string> <!-- Content description for the camera and mic off icon in the dream overlay status bar [CHAR LIMIT=NONE] --> <string name="dream_overlay_status_bar_camera_mic_off">Camera and mic are off</string> <!-- Content description for the notifications indicator icon in the dream overlay status bar [CHAR LIMIT=NONE] --> @@ -2560,6 +2566,10 @@ =1 {# notification} other {# notifications} }</string> + <!-- Accessibility label for weather complication on dreams with weather condition and temperature [CHAR_LIMIT=200] --> + <string name="dream_overlay_weather_complication_desc"> + <xliff:g id="weather_condition" example="Partly cloudy">%1$s</xliff:g>, <xliff:g id="temperature" example="7°C">%2$s</xliff:g> + </string> <!-- [CHAR LIMIT=NONE] Le audio broadcast dialog, media app is broadcasting --> <string name="broadcasting_description_is_broadcasting">Broadcasting</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index f954bc9bf17c..112d903c609c 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -265,6 +265,10 @@ <style name="Animation.ShutdownUi" parent="@android:style/Animation.Toast"> </style> + <style name="Theme.SystemUI.MediaProjectionAppSelector" + parent="@*android:style/Theme.DeviceDefault.Chooser"> + </style> + <!-- Standard animations for hiding and showing the status bar. --> <style name="Theme.SystemUI" parent="@*android:style/Theme.DeviceDefault.SystemUI"> diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml index eab7defe1e52..9115d42fc42d 100644 --- a/packages/SystemUI/res/xml/media_session_collapsed.xml +++ b/packages/SystemUI/res/xml/media_session_collapsed.xml @@ -19,7 +19,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto"> <Constraint - android:id="@+id/media_action_barrier" + android:id="@+id/media_action_barrier_start" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintTop_toBottomOf="@id/media_seamless" @@ -91,12 +91,16 @@ app:layout_constraintRight_toLeftOf="@id/media_progress_bar" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@id/media_seamless" - app:layout_constraintLeft_toRightOf="@id/media_action_barrier" /> + app:layout_constraintLeft_toRightOf="@id/media_action_barrier_start" /> <!-- Showing time while scrubbing isn't available in collapsed mode. --> <Constraint android:id="@+id/media_scrubbing_elapsed_time" - android:visibility="gone" /> + android:visibility="gone" + app:layout_constraintRight_toLeftOf="@id/media_progress_bar" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/media_seamless" + app:layout_constraintLeft_toRightOf="@id/media_action_barrier_start" /> <Constraint android:id="@+id/media_progress_bar" @@ -124,7 +128,12 @@ <!-- Showing time while scrubbing isn't available in collapsed mode. --> <Constraint android:id="@+id/media_scrubbing_total_time" - android:visibility="gone" /> + android:visibility="gone" + app:layout_constraintVertical_bias="1" + app:layout_constraintLeft_toRightOf="@id/media_progress_bar" + app:layout_constraintRight_toLeftOf="@id/action0" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintTop_toBottomOf="@id/media_seamless" /> <Constraint android:id="@+id/action0" diff --git a/packages/SystemUI/screenshot/Android.bp b/packages/SystemUI/screenshot/Android.bp index a79fd9040db3..f449398fc9f8 100644 --- a/packages/SystemUI/screenshot/Android.bp +++ b/packages/SystemUI/screenshot/Android.bp @@ -26,11 +26,7 @@ android_library { manifest: "AndroidManifest.xml", srcs: [ - // All files in this library should be in Kotlin besides some exceptions. "src/**/*.kt", - - // This file was forked from google3, so exceptionally it can be in Java. - "src/com/android/systemui/testing/screenshot/DynamicColorsTestUtils.java", ], resource_dirs: [ @@ -42,6 +38,7 @@ android_library { "androidx.test.espresso.core", "androidx.appcompat_appcompat", "platform-screenshot-diff-core", + "guava", ], kotlincflags: ["-Xjvm-default=all"], diff --git a/packages/SystemUI/screenshot/AndroidManifest.xml b/packages/SystemUI/screenshot/AndroidManifest.xml index 3b703be34e5d..a405836bd77f 100644 --- a/packages/SystemUI/screenshot/AndroidManifest.xml +++ b/packages/SystemUI/screenshot/AndroidManifest.xml @@ -23,6 +23,4 @@ android:exported="true" android:theme="@style/Theme.SystemUI.Screenshot" /> </application> - - <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> </manifest> diff --git a/packages/SystemUI/screenshot/res/values/themes.xml b/packages/SystemUI/screenshot/res/values/themes.xml index 40e50bbb6bbf..a7f8a264e892 100644 --- a/packages/SystemUI/screenshot/res/values/themes.xml +++ b/packages/SystemUI/screenshot/res/values/themes.xml @@ -19,6 +19,12 @@ <item name="android:windowActionBar">false</item> <item name="android:windowNoTitle">true</item> + <!-- We make the status and navigation bars transparent so that the screenshotted content is + not clipped by the status bar height when drawn into the Bitmap (which is what happens + given that we draw the view into the Bitmap using hardware acceleration). --> + <item name="android:statusBarColor">@android:color/transparent</item> + <item name="android:navigationBarColor">@android:color/transparent</item> + <!-- Make sure that device specific cutouts don't impact the outcome of screenshot tests --> <item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item> </style> diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt new file mode 100644 index 000000000000..a4a70a49fce3 --- /dev/null +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/Bitmap.kt @@ -0,0 +1,66 @@ +/* + * 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.testing.screenshot + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.os.Build +import android.view.View +import platform.test.screenshot.matchers.MSSIMMatcher +import platform.test.screenshot.matchers.PixelPerfectMatcher + +/** Draw this [View] into a [Bitmap]. */ +// TODO(b/195673633): Remove this once Compose screenshot tests use hardware rendering for their +// tests. +fun View.drawIntoBitmap(): Bitmap { + val bitmap = + Bitmap.createBitmap( + measuredWidth, + measuredHeight, + Bitmap.Config.ARGB_8888, + ) + val canvas = Canvas(bitmap) + draw(canvas) + return bitmap +} + +/** + * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for + * screenshot *unit* tests. + */ +val UnitTestBitmapMatcher = + if (Build.CPU_ABI == "x86_64") { + // Different CPU architectures can sometimes end up rendering differently, so we can't do + // pixel-perfect matching on different architectures using the same golden. Given that our + // presubmits are run on cf_x86_64_phone, our goldens should be perfectly matched on the + // x86_64 architecture and use the Structural Similarity Index on others. + // TODO(b/237511747): Run our screenshot presubmit tests on arm64 instead so that we can + // do pixel perfect matching both at presubmit time and at development time with actual + // devices. + PixelPerfectMatcher() + } else { + MSSIMMatcher() + } + +/** + * The [BitmapMatcher][platform.test.screenshot.matchers.BitmapMatcher] that should be used for + * screenshot *unit* tests. + * + * We use the Structural Similarity Index for integration tests because they usually contain + * additional information and noise that shouldn't break the test. + */ +val IntegrationTestBitmapMatcher = MSSIMMatcher() diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/DynamicColorsTestUtils.java b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/DynamicColorsTestUtils.java deleted file mode 100644 index 96ec4c543474..000000000000 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/DynamicColorsTestUtils.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * 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.testing.screenshot; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import android.app.UiAutomation; -import android.content.Context; -import android.provider.Settings; -import android.util.Log; - -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.NonNull; -import androidx.annotation.RequiresApi; -import androidx.core.content.ContextCompat; -import androidx.test.espresso.Espresso; -import androidx.test.espresso.IdlingRegistry; -import androidx.test.espresso.IdlingResource; - -import org.json.JSONObject; -import org.junit.function.ThrowingRunnable; - -import java.util.HashMap; -import java.util.Map; - -/* - * Note: This file was forked from - * google3/third_party/java_src/android_libs/material_components/screenshot_tests/java/android/ - * support/design/scuba/color/DynamicColorsTestUtils.java. - */ - -/** Utility that helps change the dynamic system colors for testing. */ -@RequiresApi(32) -public class DynamicColorsTestUtils { - - private static final String TAG = DynamicColorsTestUtils.class.getSimpleName(); - - private static final String THEME_CUSTOMIZATION_KEY = "theme_customization_overlay_packages"; - private static final String THEME_CUSTOMIZATION_SYSTEM_PALETTE_KEY = - "android.theme.customization.system_palette"; - - private static final int ORANGE_SYSTEM_SEED_COLOR = 0xA66800; - private static final int ORANGE_EXPECTED_SYSTEM_ACCENT1_600_COLOR = -8235756; - - private DynamicColorsTestUtils() { - } - - /** - * Update system dynamic colors (e.g., android.R.color.system_accent1_600) based on an orange - * seed color, and then wait for the change to propagate to the app by comparing - * android.R.color.system_accent1_600 to the expected orange value. - */ - public static void updateSystemColorsToOrange() { - updateSystemColors(ORANGE_SYSTEM_SEED_COLOR, ORANGE_EXPECTED_SYSTEM_ACCENT1_600_COLOR); - } - - /** - * Update system dynamic colors (e.g., android.R.color.system_accent1_600) based on the provided - * {@code seedColor}, and then wait for the change to propagate to the app by comparing - * android.R.color.system_accent1_600 to {@code expectedSystemAccent1600}. - */ - public static void updateSystemColors( - @ColorInt int seedColor, @ColorInt int expectedSystemAccent1600) { - Context context = getInstrumentation().getTargetContext(); - - int actualSystemAccent1600 = - ContextCompat.getColor(context, android.R.color.system_accent1_600); - - if (expectedSystemAccent1600 == actualSystemAccent1600) { - String expectedColorString = Integer.toHexString(expectedSystemAccent1600); - Log.d( - TAG, - "Skipped updating system colors since system_accent1_600 is already equal to " - + "expected: " - + expectedColorString); - return; - } - - updateSystemColors(seedColor); - } - - /** - * Update system dynamic colors (e.g., android.R.color.system_accent1_600) based on the provided - * {@code seedColor}, and then wait for the change to propagate to the app by checking - * android.R.color.system_accent1_600 for any change. - */ - public static void updateSystemColors(@ColorInt int seedColor) { - Context context = getInstrumentation().getTargetContext(); - - // Initialize system color idling resource with original system_accent1_600 value. - ColorChangeIdlingResource systemColorIdlingResource = - new ColorChangeIdlingResource(context, android.R.color.system_accent1_600); - - // Update system theme color setting to trigger fabricated resource overlay. - runWithShellPermissionIdentity( - () -> - Settings.Secure.putString( - context.getContentResolver(), - THEME_CUSTOMIZATION_KEY, - buildThemeCustomizationString(seedColor))); - - // Wait for system color update to propagate to app. - IdlingRegistry idlingRegistry = IdlingRegistry.getInstance(); - idlingRegistry.register(systemColorIdlingResource); - Espresso.onIdle(); - idlingRegistry.unregister(systemColorIdlingResource); - - Log.d(TAG, - Settings.Secure.getString(context.getContentResolver(), THEME_CUSTOMIZATION_KEY)); - } - - private static String buildThemeCustomizationString(@ColorInt int seedColor) { - String seedColorHex = Integer.toHexString(seedColor); - Map<String, String> themeCustomizationMap = new HashMap<>(); - themeCustomizationMap.put(THEME_CUSTOMIZATION_SYSTEM_PALETTE_KEY, seedColorHex); - return new JSONObject(themeCustomizationMap).toString(); - } - - private static void runWithShellPermissionIdentity(@NonNull ThrowingRunnable runnable) { - UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); - uiAutomation.adoptShellPermissionIdentity(); - try { - runnable.run(); - } catch (Throwable e) { - throw new RuntimeException(e); - } finally { - uiAutomation.dropShellPermissionIdentity(); - } - } - - private static class ColorChangeIdlingResource implements IdlingResource { - - private final Context mContext; - private final int mColorResId; - private final int mInitialColorInt; - - private ResourceCallback mResourceCallback; - private boolean mIdleNow; - - ColorChangeIdlingResource(Context context, @ColorRes int colorResId) { - this.mContext = context; - this.mColorResId = colorResId; - this.mInitialColorInt = ContextCompat.getColor(context, colorResId); - } - - @Override - public String getName() { - return ColorChangeIdlingResource.class.getName(); - } - - @Override - public boolean isIdleNow() { - if (mIdleNow) { - return true; - } - - int currentColorInt = ContextCompat.getColor(mContext, mColorResId); - - String initialColorString = Integer.toHexString(mInitialColorInt); - String currentColorString = Integer.toHexString(currentColorInt); - Log.d(TAG, String.format("Initial=%s, Current=%s", initialColorString, - currentColorString)); - - mIdleNow = currentColorInt != mInitialColorInt; - Log.d(TAG, String.format("idleNow=%b", mIdleNow)); - - if (mIdleNow) { - mResourceCallback.onTransitionToIdle(); - } - return mIdleNow; - } - - @Override - public void registerIdleTransitionCallback(ResourceCallback resourceCallback) { - this.mResourceCallback = resourceCallback; - } - } -} diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestRule.kt deleted file mode 100644 index 564901c2a773..000000000000 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestRule.kt +++ /dev/null @@ -1,223 +0,0 @@ -/* - * 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.testing.screenshot - -import android.app.UiModeManager -import android.content.Context -import android.graphics.Bitmap -import android.graphics.Canvas -import android.os.Build -import android.os.UserHandle -import android.view.Display -import android.view.View -import android.view.WindowManagerGlobal -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.rules.TestRule -import org.junit.runner.Description -import org.junit.runners.model.Statement -import platform.test.screenshot.GoldenImagePathManager -import platform.test.screenshot.PathConfig -import platform.test.screenshot.PathElementNoContext -import platform.test.screenshot.ScreenshotTestRule -import platform.test.screenshot.matchers.MSSIMMatcher -import platform.test.screenshot.matchers.PixelPerfectMatcher - -/** - * A base rule for screenshot diff tests. - * - * This rules takes care of setting up the activity according to [testSpec] by: - * - emulating the display size and density. - * - setting the dark/light mode. - * - setting the system (Material You) colors to a fixed value. - * - * @see ComposeScreenshotTestRule - * @see ViewScreenshotTestRule - */ -class ScreenshotTestRule(private val testSpec: ScreenshotTestSpec) : TestRule { - private var currentDisplay: DisplaySpec? = null - private var currentGoldenIdentifier: String? = null - - private val pathConfig = - PathConfig( - PathElementNoContext("model", isDir = true) { - currentDisplay?.name ?: error("currentDisplay is null") - }, - ) - private val matcher = if (shouldUsePerfectMatching()) { - PixelPerfectMatcher() - } else { - MSSIMMatcher() - } - - private val screenshotRule = - ScreenshotTestRule( - SystemUIGoldenImagePathManager( - pathConfig, - currentGoldenIdentifier = { - currentGoldenIdentifier ?: error("currentGoldenIdentifier is null") - }, - ) - ) - - private fun shouldUsePerfectMatching(): Boolean { - // Different CPU architectures can sometimes end up rendering differently, so we can't do - // pixel-perfect matching on different architectures using the same golden. Given that our - // presubmits are run on cf_x86_64_phone, our goldens should be perfectly matched on the - // x86_64 architecture and use the Structural Similarity Index on others. - // TODO(b/237511747): Run our screenshot presubmit tests on arm64 instead so that we can - // do pixel perfect matching both at presubmit time and at development time with actual - // devices. - return Build.CPU_ABI == "x86_64" - } - - override fun apply(base: Statement, description: Description): Statement { - // The statement which call beforeTest() before running the test and afterTest() afterwards. - val statement = - object : Statement() { - override fun evaluate() { - try { - beforeTest() - base.evaluate() - } finally { - afterTest() - } - } - } - - return screenshotRule.apply(statement, description) - } - - private fun beforeTest() { - // Update the system colors to a fixed color, so that tests don't depend on the host device - // extracted colors. Note that we don't restore the default device colors at the end of the - // test because changing the colors (and waiting for them to be applied) is costly and makes - // the screenshot tests noticeably slower. - DynamicColorsTestUtils.updateSystemColorsToOrange() - - // Emulate the display size and density. - val display = testSpec.display - val density = display.densityDpi - val wm = WindowManagerGlobal.getWindowManagerService() - val (width, height) = getEmulatedDisplaySize() - wm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density, UserHandle.myUserId()) - wm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, width, height) - - // Force the dark/light theme. - val uiModeManager = - InstrumentationRegistry.getInstrumentation() - .targetContext - .getSystemService(Context.UI_MODE_SERVICE) as UiModeManager - uiModeManager.setApplicationNightMode( - if (testSpec.isDarkTheme) { - UiModeManager.MODE_NIGHT_YES - } else { - UiModeManager.MODE_NIGHT_NO - } - ) - } - - private fun afterTest() { - // Reset the density and display size. - val wm = WindowManagerGlobal.getWindowManagerService() - wm.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, UserHandle.myUserId()) - wm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY) - - // Reset the dark/light theme. - val uiModeManager = - InstrumentationRegistry.getInstrumentation() - .targetContext - .getSystemService(Context.UI_MODE_SERVICE) as UiModeManager - uiModeManager.setApplicationNightMode(UiModeManager.MODE_NIGHT_AUTO) - } - - /** - * Compare the content of [view] with the golden image identified by [goldenIdentifier] in the - * context of [testSpec]. - */ - fun screenshotTest(goldenIdentifier: String, view: View) { - val bitmap = drawIntoBitmap(view) - - // Compare bitmap against golden asset. - val isDarkTheme = testSpec.isDarkTheme - val isLandscape = testSpec.isLandscape - val identifierWithSpec = buildString { - append(goldenIdentifier) - if (isDarkTheme) append("_dark") - if (isLandscape) append("_landscape") - } - - // TODO(b/230832101): Provide a way to pass a PathConfig and override the file name on - // device to assertBitmapAgainstGolden instead? - currentDisplay = testSpec.display - currentGoldenIdentifier = goldenIdentifier - screenshotRule.assertBitmapAgainstGolden(bitmap, identifierWithSpec, matcher) - currentDisplay = null - currentGoldenIdentifier = goldenIdentifier - } - - /** Draw [view] into a [Bitmap]. */ - private fun drawIntoBitmap(view: View): Bitmap { - val bitmap = - Bitmap.createBitmap( - view.measuredWidth, - view.measuredHeight, - Bitmap.Config.ARGB_8888, - ) - val canvas = Canvas(bitmap) - view.draw(canvas) - return bitmap - } - - /** Get the emulated display size for [testSpec]. */ - private fun getEmulatedDisplaySize(): Pair<Int, Int> { - val display = testSpec.display - val isPortraitNaturalPosition = display.width < display.height - return if (testSpec.isLandscape) { - if (isPortraitNaturalPosition) { - display.height to display.width - } else { - display.width to display.height - } - } else { - if (isPortraitNaturalPosition) { - display.width to display.height - } else { - display.height to display.width - } - } - } -} - -private class SystemUIGoldenImagePathManager( - pathConfig: PathConfig, - private val currentGoldenIdentifier: () -> String, -) : - GoldenImagePathManager( - appContext = InstrumentationRegistry.getInstrumentation().context, - deviceLocalPath = - InstrumentationRegistry.getInstrumentation() - .targetContext - .filesDir - .absolutePath - .toString() + "/sysui_screenshots", - pathConfig = pathConfig, - ) { - // This string is appended to all actual/expected screenshots on the device. We append the - // golden identifier so that our pull_golden.py scripts can map a screenshot on device to its - // asset (and automatically update it, if necessary). - override fun toString() = currentGoldenIdentifier() -} diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestSpec.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestSpec.kt deleted file mode 100644 index 7fc624554738..000000000000 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ScreenshotTestSpec.kt +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.testing.screenshot - -/** The specification of a device display to be used in a screenshot test. */ -data class DisplaySpec( - val name: String, - val width: Int, - val height: Int, - val densityDpi: Int, -) - -/** The specification of a screenshot diff test. */ -class ScreenshotTestSpec( - val display: DisplaySpec, - val isDarkTheme: Boolean = false, - val isLandscape: Boolean = false, -) { - companion object { - /** - * Return a list of [ScreenshotTestSpec] for each of the [displays]. - * - * If [isDarkTheme] is null, this will create a spec for both light and dark themes, for - * each of the orientation. - * - * If [isLandscape] is null, this will create a spec for both portrait and landscape, for - * each of the light/dark themes. - */ - fun forDisplays( - vararg displays: DisplaySpec, - isDarkTheme: Boolean? = null, - isLandscape: Boolean? = null, - ): List<ScreenshotTestSpec> { - return displays.flatMap { display -> - buildList { - fun addDisplay(isLandscape: Boolean) { - if (isDarkTheme != true) { - add(ScreenshotTestSpec(display, isDarkTheme = false, isLandscape)) - } - - if (isDarkTheme != false) { - add(ScreenshotTestSpec(display, isDarkTheme = true, isLandscape)) - } - } - - if (isLandscape != true) { - addDisplay(isLandscape = false) - } - - if (isLandscape != false) { - addDisplay(isLandscape = true) - } - } - } - } - } - - override fun toString(): String = buildString { - // This string is appended to PNGs stored in the device, so let's keep it simple. - append(display.name) - if (isDarkTheme) append("_dark") - if (isLandscape) append("_landscape") - } -} diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt new file mode 100644 index 000000000000..cbab0a75061e --- /dev/null +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt @@ -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 com.android.systemui.testing.screenshot + +import androidx.test.platform.app.InstrumentationRegistry +import platform.test.screenshot.GoldenImagePathManager +import platform.test.screenshot.PathConfig + +/** A [GoldenImagePathManager] that should be used for all SystemUI screenshot tests. */ +class SystemUIGoldenImagePathManager( + pathConfig: PathConfig, +) : + GoldenImagePathManager( + appContext = InstrumentationRegistry.getInstrumentation().context, + deviceLocalPath = + InstrumentationRegistry.getInstrumentation() + .targetContext + .filesDir + .absolutePath + .toString() + "/sysui_screenshots", + pathConfig = pathConfig, + ) { + override fun toString(): String { + // This string is appended to all actual/expected screenshots on the device, so make sure + // it is a static value. + return "SystemUIGoldenImagePathManager" + } +} diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt new file mode 100644 index 000000000000..c609e6f8b4bf --- /dev/null +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt @@ -0,0 +1,180 @@ +package com.android.systemui.testing.screenshot + +import android.app.Activity +import android.content.Context +import android.content.ContextWrapper +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Rect +import android.os.Build +import android.os.Handler +import android.os.Looper +import android.util.Log +import android.view.PixelCopy +import android.view.SurfaceView +import android.view.View +import android.view.ViewTreeObserver +import android.view.Window +import androidx.annotation.RequiresApi +import androidx.concurrent.futures.ResolvableFuture +import androidx.test.annotation.ExperimentalTestApi +import androidx.test.core.internal.os.HandlerExecutor +import androidx.test.platform.graphics.HardwareRendererCompat +import com.google.common.util.concurrent.ListenableFuture + +/* + * This file was forked from androidx/test/core/view/ViewCapture.kt to add [Window] parameter to + * [View.captureToBitmap]. + * TODO(b/195673633): Remove this fork and use the AndroidX version instead. + */ + +/** + * Asynchronously captures an image of the underlying view into a [Bitmap]. + * + * For devices below [Build.VERSION_CODES#O] (or if the view's window cannot be determined), the + * image is obtained using [View#draw]. Otherwise, [PixelCopy] is used. + * + * This method will also enable [HardwareRendererCompat#setDrawingEnabled(boolean)] if required. + * + * This API is primarily intended for use in lower layer libraries or frameworks. For test authors, + * its recommended to use espresso or compose's captureToImage. + * + * This API is currently experimental and subject to change or removal. + */ +@ExperimentalTestApi +@RequiresApi(Build.VERSION_CODES.JELLY_BEAN) +fun View.captureToBitmap(window: Window? = null): ListenableFuture<Bitmap> { + val bitmapFuture: ResolvableFuture<Bitmap> = ResolvableFuture.create() + val mainExecutor = HandlerExecutor(Handler(Looper.getMainLooper())) + + // disable drawing again if necessary once work is complete + if (!HardwareRendererCompat.isDrawingEnabled()) { + HardwareRendererCompat.setDrawingEnabled(true) + bitmapFuture.addListener({ HardwareRendererCompat.setDrawingEnabled(false) }, mainExecutor) + } + + mainExecutor.execute { + val forceRedrawFuture = forceRedraw() + forceRedrawFuture.addListener({ generateBitmap(bitmapFuture, window) }, mainExecutor) + } + + return bitmapFuture +} + +/** + * Trigger a redraw of the given view. + * + * Should only be called on UI thread. + * + * @return a [ListenableFuture] that will be complete once ui drawing is complete + */ +// NoClassDefFoundError occurs on API 15 +@RequiresApi(Build.VERSION_CODES.JELLY_BEAN) +// @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +@ExperimentalTestApi +fun View.forceRedraw(): ListenableFuture<Void> { + val future: ResolvableFuture<Void> = ResolvableFuture.create() + + if (Build.VERSION.SDK_INT >= 29 && isHardwareAccelerated) { + viewTreeObserver.registerFrameCommitCallback() { future.set(null) } + } else { + viewTreeObserver.addOnDrawListener( + object : ViewTreeObserver.OnDrawListener { + var handled = false + override fun onDraw() { + if (!handled) { + handled = true + future.set(null) + // cannot remove on draw listener inside of onDraw + Handler(Looper.getMainLooper()).post { + viewTreeObserver.removeOnDrawListener(this) + } + } + } + } + ) + } + invalidate() + return future +} + +private fun View.generateBitmap( + bitmapFuture: ResolvableFuture<Bitmap>, + window: Window? = null, +) { + if (bitmapFuture.isCancelled) { + return + } + val destBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) + when { + Build.VERSION.SDK_INT < 26 -> generateBitmapFromDraw(destBitmap, bitmapFuture) + this is SurfaceView -> generateBitmapFromSurfaceViewPixelCopy(destBitmap, bitmapFuture) + else -> { + val window = window ?: getActivity()?.window + if (window != null) { + generateBitmapFromPixelCopy(window, destBitmap, bitmapFuture) + } else { + Log.i( + "View.captureToImage", + "Could not find window for view. Falling back to View#draw instead of PixelCopy" + ) + generateBitmapFromDraw(destBitmap, bitmapFuture) + } + } + } +} + +@SuppressWarnings("NewApi") +private fun SurfaceView.generateBitmapFromSurfaceViewPixelCopy( + destBitmap: Bitmap, + bitmapFuture: ResolvableFuture<Bitmap> +) { + val onCopyFinished = + PixelCopy.OnPixelCopyFinishedListener { result -> + if (result == PixelCopy.SUCCESS) { + bitmapFuture.set(destBitmap) + } else { + bitmapFuture.setException( + RuntimeException(String.format("PixelCopy failed: %d", result)) + ) + } + } + PixelCopy.request(this, null, destBitmap, onCopyFinished, handler) +} + +internal fun View.generateBitmapFromDraw( + destBitmap: Bitmap, + bitmapFuture: ResolvableFuture<Bitmap> +) { + destBitmap.density = resources.displayMetrics.densityDpi + computeScroll() + val canvas = Canvas(destBitmap) + canvas.translate((-scrollX).toFloat(), (-scrollY).toFloat()) + draw(canvas) + bitmapFuture.set(destBitmap) +} + +private fun View.getActivity(): Activity? { + fun Context.getActivity(): Activity? { + return when (this) { + is Activity -> this + is ContextWrapper -> this.baseContext.getActivity() + else -> null + } + } + return context.getActivity() +} + +private fun View.generateBitmapFromPixelCopy( + window: Window, + destBitmap: Bitmap, + bitmapFuture: ResolvableFuture<Bitmap> +) { + val locationInWindow = intArrayOf(0, 0) + getLocationInWindow(locationInWindow) + val x = locationInWindow[0] + val y = locationInWindow[1] + val boundsInWindow = Rect(x, y, x + width, y + height) + + return window.generateBitmapFromPixelCopy(boundsInWindow, destBitmap, bitmapFuture) +} diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt index 6a80c486d515..60130e1086ef 100644 --- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewScreenshotTestRule.kt @@ -1,58 +1,114 @@ +/* + * 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.testing.screenshot import android.app.Activity import android.app.Dialog +import android.graphics.Bitmap +import android.graphics.HardwareRenderer +import android.os.Looper import android.view.View import android.view.ViewGroup import android.view.ViewGroup.LayoutParams +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.view.ViewGroup.LayoutParams.WRAP_CONTENT +import android.view.Window +import androidx.activity.ComponentActivity +import androidx.test.espresso.Espresso import androidx.test.ext.junit.rules.ActivityScenarioRule +import com.google.common.util.concurrent.FutureCallback +import com.google.common.util.concurrent.Futures +import kotlin.coroutines.suspendCoroutine +import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.rules.RuleChain import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement +import platform.test.screenshot.DeviceEmulationRule +import platform.test.screenshot.DeviceEmulationSpec +import platform.test.screenshot.MaterialYouColorsRule +import platform.test.screenshot.ScreenshotTestRule +import platform.test.screenshot.getEmulatedDevicePathConfig -/** A rule for View screenshot diff tests. */ -class ViewScreenshotTestRule(testSpec: ScreenshotTestSpec) : TestRule { +/** A rule for View screenshot diff unit tests. */ +class ViewScreenshotTestRule(emulationSpec: DeviceEmulationSpec) : TestRule { + private val colorsRule = MaterialYouColorsRule() + private val deviceEmulationRule = DeviceEmulationRule(emulationSpec) + private val screenshotRule = + ScreenshotTestRule( + SystemUIGoldenImagePathManager(getEmulatedDevicePathConfig(emulationSpec)) + ) private val activityRule = ActivityScenarioRule(ScreenshotActivity::class.java) - private val screenshotRule = ScreenshotTestRule(testSpec) - - private val delegate = RuleChain.outerRule(screenshotRule).around(activityRule) + private val delegateRule = + RuleChain.outerRule(colorsRule) + .around(deviceEmulationRule) + .around(screenshotRule) + .around(activityRule) + private val matcher = UnitTestBitmapMatcher override fun apply(base: Statement, description: Description): Statement { - return delegate.apply(base, description) + return delegateRule.apply(base, description) } /** * Compare the content of the view provided by [viewProvider] with the golden image identified - * by [goldenIdentifier] in the context of [testSpec]. + * by [goldenIdentifier] in the context of [emulationSpec]. */ fun screenshotTest( goldenIdentifier: String, - layoutParams: LayoutParams = - LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT), - viewProvider: (Activity) -> View, + mode: Mode = Mode.WrapContent, + viewProvider: (ComponentActivity) -> View, ) { activityRule.scenario.onActivity { activity -> // Make sure that the activity draws full screen and fits the whole display instead of // the system bars. - activity.window.setDecorFitsSystemWindows(false) - activity.setContentView(viewProvider(activity), layoutParams) + val window = activity.window + window.setDecorFitsSystemWindows(false) + + // Set the content. + activity.setContentView(viewProvider(activity), mode.layoutParams) + + // Elevation/shadows is not deterministic when doing hardware rendering, so we disable + // it for any view in the hierarchy. + window.decorView.removeElevationRecursively() } // We call onActivity again because it will make sure that our Activity is done measuring, // laying out and drawing its content (that we set in the previous onActivity lambda). + var contentView: View? = null activityRule.scenario.onActivity { activity -> // Check that the content is what we expected. val content = activity.requireViewById<ViewGroup>(android.R.id.content) assertEquals(1, content.childCount) - screenshotRule.screenshotTest(goldenIdentifier, content.getChildAt(0)) + contentView = content.getChildAt(0) } + + val bitmap = contentView?.toBitmap() ?: error("contentView is null") + screenshotRule.assertBitmapAgainstGolden( + bitmap, + goldenIdentifier, + matcher, + ) } /** * Compare the content of the dialog provided by [dialogProvider] with the golden image - * identified by [goldenIdentifier] in the context of [testSpec]. + * identified by [goldenIdentifier] in the context of [emulationSpec]. */ fun dialogScreenshotTest( goldenIdentifier: String, @@ -70,21 +126,78 @@ class ViewScreenshotTestRule(testSpec: ScreenshotTestSpec) : TestRule { create() window.setWindowAnimations(0) + // Elevation/shadows is not deterministic when doing hardware rendering, so we + // disable it for any view in the hierarchy. + window.decorView.removeElevationRecursively() + // Show the dialog. show() } } - // We call onActivity again because it will make sure that our Dialog is done measuring, - // laying out and drawing its content (that we set in the previous onActivity lambda). - activityRule.scenario.onActivity { - // Check that the content is what we expected. - val dialog = dialog ?: error("dialog is null") - try { - screenshotRule.screenshotTest(goldenIdentifier, dialog.window.decorView) - } finally { - dialog.dismiss() + try { + val bitmap = dialog?.toBitmap() ?: error("dialog is null") + screenshotRule.assertBitmapAgainstGolden( + bitmap, + goldenIdentifier, + matcher, + ) + } finally { + dialog?.dismiss() + } + } + + private fun View.removeElevationRecursively() { + this.elevation = 0f + + if (this is ViewGroup) { + repeat(childCount) { i -> getChildAt(i).removeElevationRecursively() } + } + } + + private fun Dialog.toBitmap(): Bitmap { + val window = window + return window.decorView.toBitmap(window) + } + + private fun View.toBitmap(window: Window? = null): Bitmap { + if (Looper.getMainLooper() == Looper.myLooper()) { + error("toBitmap() can't be called from the main thread") + } + + if (!HardwareRenderer.isDrawingEnabled()) { + error("Hardware rendering is not enabled") + } + + // Make sure we are idle. + Espresso.onIdle() + + val mainExecutor = context.mainExecutor + return runBlocking { + suspendCoroutine { continuation -> + Futures.addCallback( + captureToBitmap(window), + object : FutureCallback<Bitmap> { + override fun onSuccess(result: Bitmap?) { + continuation.resumeWith(Result.success(result!!)) + } + + override fun onFailure(t: Throwable) { + continuation.resumeWith(Result.failure(t)) + } + }, + // We know that we are not on the main thread, so we can block the current + // thread and wait for the result in the main thread. + mainExecutor, + ) } } } + + enum class Mode(val layoutParams: LayoutParams) { + WrapContent(LayoutParams(WRAP_CONTENT, WRAP_CONTENT)), + MatchSize(LayoutParams(MATCH_PARENT, MATCH_PARENT)), + MatchWidth(LayoutParams(MATCH_PARENT, WRAP_CONTENT)), + MatchHeight(LayoutParams(WRAP_CONTENT, MATCH_PARENT)), + } } diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt new file mode 100644 index 000000000000..d34f46bf48a6 --- /dev/null +++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/WindowCapture.kt @@ -0,0 +1,37 @@ +package com.android.systemui.testing.screenshot + +import android.graphics.Bitmap +import android.graphics.Rect +import android.os.Handler +import android.os.Looper +import android.view.PixelCopy +import android.view.Window +import androidx.concurrent.futures.ResolvableFuture + +/* + * This file was forked from androidx/test/core/view/WindowCapture.kt. + * TODO(b/195673633): Remove this fork and use the AndroidX version instead. + */ +fun Window.generateBitmapFromPixelCopy( + boundsInWindow: Rect? = null, + destBitmap: Bitmap, + bitmapFuture: ResolvableFuture<Bitmap> +) { + val onCopyFinished = + PixelCopy.OnPixelCopyFinishedListener { result -> + if (result == PixelCopy.SUCCESS) { + bitmapFuture.set(destBitmap) + } else { + bitmapFuture.setException( + RuntimeException(String.format("PixelCopy failed: %d", result)) + ) + } + } + PixelCopy.request( + this, + boundsInWindow, + destBitmap, + onCopyFinished, + Handler(Looper.getMainLooper()) + ) +} diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 9f790c66302d..114ea657a758 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -50,6 +50,7 @@ android_library { "SystemUIUnfoldLib", "androidx.dynamicanimation_dynamicanimation", "androidx.concurrent_concurrent-futures", + "gson-prebuilt-jar", "dagger2", "jsr330", ], diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt index b4646aee444c..a8a526a33229 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt @@ -29,6 +29,7 @@ interface Flag<T> { interface ParcelableFlag<T> : Flag<T>, Parcelable { val default: T + val overridden: Boolean override fun describeContents() = 0 } @@ -52,7 +53,8 @@ interface SysPropFlag<T> : Flag<T> { data class BooleanFlag @JvmOverloads constructor( override val id: Int, override val default: Boolean = false, - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<Boolean> { companion object { @@ -65,12 +67,16 @@ data class BooleanFlag @JvmOverloads constructor( private constructor(parcel: Parcel) : this( id = parcel.readInt(), - default = parcel.readBoolean() + default = parcel.readBoolean(), + teamfood = parcel.readBoolean(), + overridden = parcel.readBoolean() ) override fun writeToParcel(parcel: Parcel, flags: Int) { parcel.writeInt(id) parcel.writeBoolean(default) + parcel.writeBoolean(teamfood) + parcel.writeBoolean(overridden) } } @@ -100,7 +106,8 @@ data class SysPropBooleanFlag @JvmOverloads constructor( data class StringFlag @JvmOverloads constructor( override val id: Int, override val default: String = "", - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<String> { companion object { @JvmField @@ -130,7 +137,8 @@ data class ResourceStringFlag @JvmOverloads constructor( data class IntFlag @JvmOverloads constructor( override val id: Int, override val default: Int = 0, - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<Int> { companion object { @@ -161,7 +169,8 @@ data class ResourceIntFlag @JvmOverloads constructor( data class LongFlag @JvmOverloads constructor( override val id: Int, override val default: Long = 0, - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<Long> { companion object { @@ -186,7 +195,8 @@ data class LongFlag @JvmOverloads constructor( data class FloatFlag @JvmOverloads constructor( override val id: Int, override val default: Float = 0f, - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<Float> { companion object { @@ -217,7 +227,8 @@ data class ResourceFloatFlag @JvmOverloads constructor( data class DoubleFlag @JvmOverloads constructor( override val id: Int, override val default: Double = 0.0, - override val teamfood: Boolean = false + override val teamfood: Boolean = false, + override val overridden: Boolean = false ) : ParcelableFlag<Double> { companion object { @@ -237,4 +248,4 @@ data class DoubleFlag @JvmOverloads constructor( parcel.writeInt(id) parcel.writeDouble(default) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt index 26e40e1ecad3..d172690e2488 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt @@ -64,20 +64,29 @@ class FlagManager constructor( intent.setPackage(RECEIVING_PACKAGE) return CallbackToFutureAdapter.getFuture { - completer: CallbackToFutureAdapter.Completer<Collection<Flag<*>>> -> - context.sendOrderedBroadcast(intent, null, - object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val extras: Bundle? = getResultExtras(false) - val listOfFlags: java.util.ArrayList<ParcelableFlag<*>>? = - extras?.getParcelableArrayList(EXTRA_FLAGS) - if (listOfFlags != null) { - completer.set(listOfFlags) - } else { - completer.setException(NoFlagResultsException()) - } + completer: CallbackToFutureAdapter.Completer<Collection<Flag<*>>> -> + context.sendOrderedBroadcast( + intent, + null, + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val extras: Bundle? = getResultExtras(false) + val listOfFlags: java.util.ArrayList<ParcelableFlag<*>>? = + extras?.getParcelableArrayList( + EXTRA_FLAGS, ParcelableFlag::class.java + ) + if (listOfFlags != null) { + completer.set(listOfFlags) + } else { + completer.setException(NoFlagResultsException()) } - }, null, Activity.RESULT_OK, "extra data", null) + } + }, + null, + Activity.RESULT_OK, + "extra data", + null + ) "QueryingFlags" } } @@ -152,7 +161,11 @@ class FlagManager constructor( } val parts = uri.pathSegments val idStr = parts[parts.size - 1] - val id = try { idStr.toInt() } catch (e: NumberFormatException) { return } + val id = try { + idStr.toInt() + } catch (e: NumberFormatException) { + return + } clearCacheAction?.accept(id) dispatchListenersAndMaybeRestart(id, onSettingsChangedAction) } @@ -188,4 +201,5 @@ class FlagManager constructor( } class NoFlagResultsException : Exception( - "SystemUI failed to communicate its flags back successfully")
\ No newline at end of file + "SystemUI failed to communicate its flags back successfully" +) diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockProviderPlugin.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockProviderPlugin.kt new file mode 100644 index 000000000000..916a557d7af8 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockProviderPlugin.kt @@ -0,0 +1,65 @@ +/* + * 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.clocks + +import com.android.systemui.plugins.Plugin +import com.android.systemui.plugins.annotations.ProvidesInterface +import android.annotation.FloatRange +import android.graphics.drawable.Drawable +import android.view.View + +/** Identifies a clock design */ +typealias ClockId = String + +/** A Plugin which exposes the ClockProvider interface */ +@ProvidesInterface(action = ClockProviderPlugin.ACTION, version = ClockProviderPlugin.VERSION) +interface ClockProviderPlugin : Plugin, ClockProvider { + companion object { + const val ACTION = "com.android.systemui.action.PLUGIN_CLOCK_PROVIDER" + const val VERSION = 1 + } +} + +/** Interface for building clocks and providing information about those clocks */ +interface ClockProvider { + /** Returns metadata for all clocks this provider knows about */ + fun getClocks(): List<ClockMetadata> + + /** Initializes and returns the target clock design */ + fun createClock(id: ClockId): Clock + + /** A static thumbnail for rendering in some examples */ + fun getClockThumbnail(id: ClockId): Drawable? +} + +/** Interface for controlling an active clock */ +interface Clock { + /** A small version of the clock, appropriate for smaller viewports */ + val smallClock: View + + /** A large version of the clock, appropriate when a bigger viewport is available */ + val largeClock: View + + /** Callback to update the clock view to the current time */ + fun onTimeTick() + + /** Sets the level of the AOD transition */ + fun setAodFraction(@FloatRange(from = 0.0, to = 1.0) fraction: Float) +} + +/** Some data about a clock design */ +data class ClockMetadata( + val clockId: ClockId, + val name: String +)
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt new file mode 100644 index 000000000000..32459665e6cf --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -0,0 +1,146 @@ +/* + * 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.clocks + +import android.content.Context +import android.database.ContentObserver +import android.graphics.drawable.Drawable +import android.net.Uri +import android.os.Handler +import android.os.UserHandle +import android.provider.Settings +import android.util.Log +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.plugins.PluginListener +import com.android.systemui.shared.plugins.PluginManager +import com.google.gson.Gson +import javax.inject.Inject + +private val TAG = ClockRegistry::class.simpleName +private val DEBUG = true +const val DEFAULT_CLOCK_ID = "DEFAULT" + +typealias ClockChangeListener = () -> Unit + +/** ClockRegistry aggregates providers and plugins */ +open class ClockRegistry @Inject constructor( + val context: Context, + val pluginManager: PluginManager, + @Main val handler: Handler +) { + private val gson = Gson() + private val availableClocks = mutableMapOf<ClockId, ClockInfo>() + private val clockChangeListeners = mutableListOf<ClockChangeListener>() + private val settingObserver = object : ContentObserver(handler) { + override fun onChange(selfChange: Boolean, uris: Collection<Uri>, flags: Int, userId: Int) = + clockChangeListeners.forEach { it() } + } + + private val pluginListener = object : PluginListener<ClockProviderPlugin> { + override fun onPluginConnected(plugin: ClockProviderPlugin, context: Context) { + val currentId = currentClockId + for (clock in plugin.getClocks()) { + val id = clock.clockId + val current = availableClocks[id] + if (current != null) { + Log.e(TAG, "Clock Id conflict: $id is registered by both " + + "${plugin::class.simpleName} and ${current.provider::class.simpleName}") + return + } + + availableClocks[id] = ClockInfo(clock, plugin) + + if (currentId == id) { + if (DEBUG) { + Log.i(TAG, "Current clock ($currentId) was connected") + } + clockChangeListeners.forEach { it() } + } + } + } + + override fun onPluginDisconnected(plugin: ClockProviderPlugin) { + val currentId = currentClockId + for (clock in plugin.getClocks()) { + availableClocks.remove(clock.clockId) + + if (currentId == clock.clockId) { + Log.w(TAG, "Current clock ($currentId) was disconnected") + clockChangeListeners.forEach { it() } + } + } + } + } + + open var currentClockId: ClockId + get() { + val json = Settings.Secure.getString(context.contentResolver, + Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE) + return gson.fromJson(json, ClockSetting::class.java).clockId + } + set(value) { + val json = gson.toJson(ClockSetting(value, System.currentTimeMillis())) + Settings.Secure.putString(context.contentResolver, + Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE, json) + } + + init { + pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java) + context.contentResolver.registerContentObserver( + Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE), + false, + settingObserver, + UserHandle.USER_ALL) + } + + fun getClocks(): List<ClockMetadata> = availableClocks.map { (_, clock) -> clock.metadata } + + fun getClockThumbnail(clockId: ClockId): Drawable? = + availableClocks[clockId]?.provider?.getClockThumbnail(clockId) + + fun createExampleClock(clockId: ClockId): Clock? = createClock(clockId) + + fun registerClockChangeListener(listener: ClockChangeListener) = + clockChangeListeners.add(listener) + + fun unregisterClockChangeListener(listener: ClockChangeListener) = + clockChangeListeners.remove(listener) + + fun createCurrentClock(): Clock { + val clockId = currentClockId + if (!clockId.isNullOrEmpty()) { + val clock = createClock(clockId) + if (clock != null) { + return clock + } else { + Log.e(TAG, "Clock $clockId not found; using default") + } + } + + return createClock(DEFAULT_CLOCK_ID)!! + } + + private fun createClock(clockId: ClockId): Clock? = + availableClocks[clockId]?.provider?.createClock(clockId) + + private data class ClockInfo( + val metadata: ClockMetadata, + val provider: ClockProvider + ) + + private data class ClockSetting( + val clockId: ClockId, + val _applied_timestamp: Long + ) +}
\ No newline at end of file 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 e9c1acbf7ca0..e3f568732b9d 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 @@ -18,6 +18,9 @@ package com.android.systemui.shared.recents.model; import static android.view.Display.DEFAULT_DISPLAY; +import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_ACTIVITY_TYPES; +import static com.android.wm.shell.common.split.SplitScreenConstants.CONTROLLED_WINDOWING_MODES; + import android.app.ActivityManager; import android.app.ActivityManager.TaskDescription; import android.app.TaskInfo; @@ -31,6 +34,8 @@ import android.view.ViewDebug; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.util.ArrayUtils; + import java.io.PrintWriter; import java.util.Objects; @@ -242,8 +247,10 @@ public class Task { ActivityManager.TaskDescription td = taskInfo.taskDescription; return new Task(taskKey, td != null ? td.getPrimaryColor() : 0, - td != null ? td.getBackgroundColor() : 0, - taskInfo.supportsMultiWindow, isLocked, td, taskInfo.topActivity); + td != null ? td.getBackgroundColor() : 0, taskInfo.supportsMultiWindow + && ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType()) + && ArrayUtils.contains(CONTROLLED_WINDOWING_MODES, taskInfo.getWindowingMode()), + isLocked, td, taskInfo.topActivity); } public Task(TaskKey key) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerKt.kt index b87cf47dd93f..c1429335292f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUI.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerKt.kt @@ -14,22 +14,19 @@ * limitations under the License. */ -package com.android.wm.shell.compatui; +package com.android.systemui.shared.system -import com.android.wm.shell.common.annotations.ExternalThread; +import android.app.ActivityManager + +/** Kotlin extensions for [ActivityManager] */ +object ActivityManagerKt { -/** - * Interface to engage compat UI. - */ -@ExternalThread -public interface CompatUI { /** - * Called when the keyguard showing state changes. Removes all compat UIs if the - * keyguard is now showing. - * - * <p>Note that if the keyguard is occluded it will also be considered showing. - * - * @param showing indicates if the keyguard is now showing. + * Returns `true` whether the app with the given package name has an activity at the top of the + * most recent task; `false` otherwise */ - void onKeyguardShowingChanged(boolean showing); + fun ActivityManager.isInForeground(packageName: String): Boolean { + val tasks: List<ActivityManager.RunningTaskInfo> = getRunningTasks(1) + return tasks.isNotEmpty() && packageName == tasks[0].topActivity.packageName + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index c5beaa7ee0b1..916526d0efac 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -130,13 +130,6 @@ public class ActivityManagerWrapper { } /** - * @return a list of the recents tasks. - */ - public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) { - return mAtm.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId); - } - - /** * @return a {@link ThumbnailData} with {@link TaskSnapshot} for the given {@param taskId}. * The snapshot will be triggered if no cached {@link TaskSnapshot} exists. */ @@ -247,25 +240,6 @@ public class ActivityManagerWrapper { } /** - * Starts a task from Recents. - * - * @param resultCallback The result success callback - * @param resultCallbackHandler The handler to receive the result callback - */ - public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options, - Consumer<Boolean> resultCallback, Handler resultCallbackHandler) { - final boolean result = startActivityFromRecents(taskKey, options); - if (resultCallback != null) { - resultCallbackHandler.post(new Runnable() { - @Override - public void run() { - resultCallback.accept(result); - } - }); - } - } - - /** * Starts a task from Recents synchronously. */ public boolean startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options) { @@ -286,20 +260,6 @@ public class ActivityManagerWrapper { } /** - * @deprecated use {@link TaskStackChangeListeners#registerTaskStackListener} - */ - public void registerTaskStackListener(TaskStackChangeListener listener) { - TaskStackChangeListeners.getInstance().registerTaskStackListener(listener); - } - - /** - * @deprecated use {@link TaskStackChangeListeners#unregisterTaskStackListener} - */ - public void unregisterTaskStackListener(TaskStackChangeListener listener) { - TaskStackChangeListeners.getInstance().unregisterTaskStackListener(listener); - } - - /** * Requests that the system close any open system windows (including other SystemUI). */ public void closeSystemWindows(final String reason) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java index add2d022e893..be99b270c09a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java @@ -28,14 +28,6 @@ import android.os.Handler; public abstract class ActivityOptionsCompat { /** - * @Deprecated - * @return ActivityOptions for starting a task in split screen as the primary window. - */ - public static ActivityOptions makeSplitScreenOptions(boolean dockTopLeft) { - return ActivityOptions.makeBasic(); - } - - /** * @return ActivityOptions for starting a task in freeform. */ public static ActivityOptions makeFreeformOptions() { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index 630fb360cc14..97e024238778 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -204,28 +204,6 @@ public class QuickStepContract { } /** - * Touch slopes and thresholds for quick step operations. Drag slop is the point where the - * home button press/long press over are ignored and will start to drag when exceeded and the - * touch slop is when the respected operation will occur when exceeded. Touch slop must be - * larger than the drag slop. - */ - public static int getQuickStepDragSlopPx() { - return convertDpToPixel(10); - } - - public static int getQuickStepTouchSlopPx() { - return convertDpToPixel(24); - } - - public static int getQuickScrubTouchSlopPx() { - return convertDpToPixel(24); - } - - private static int convertDpToPixel(float dp) { - return (int) (dp * Resources.getSystem().getDisplayMetrics().density); - } - - /** * Returns whether the specified sysui state is such that the assistant gesture should be * disabled. */ diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 13f1db4a0831..0094820f0dad 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -65,14 +65,6 @@ public class RecentsAnimationControllerCompat { } } - public void hideCurrentInputMethod() { - try { - mAnimationController.hideCurrentInputMethod(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to set hide input method", e); - } - } - /** * Sets the final surface transaction on a Task. This is used by Launcher to notify the system * that animating Activity to PiP has completed and the associated task surface should be 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 db416013c453..609846e8c729 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 @@ -224,6 +224,7 @@ public class RemoteTransitionCompat implements Parcelable { private WindowContainerToken mRecentsTask = null; private TransitionInfo mInfo = null; private ArrayList<SurfaceControl> mOpeningLeashes = null; + private boolean mOpeningHome = false; private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; private PictureInPictureSurfaceTransaction mPipTransaction = null; private IBinder mTransition = null; @@ -321,6 +322,7 @@ public class RemoteTransitionCompat implements Parcelable { } final int layer = mInfo.getChanges().size() * 3; mOpeningLeashes = new ArrayList<>(); + mOpeningHome = cancelRecents; final RemoteAnimationTargetCompat[] targets = new RemoteAnimationTargetCompat[openingTasks.size()]; for (int i = 0; i < openingTasks.size(); ++i) { @@ -367,10 +369,6 @@ public class RemoteTransitionCompat implements Parcelable { if (mWrapped != null) mWrapped.setAnimationTargetsBehindSystemBars(behindSystemBars); } - @Override public void hideCurrentInputMethod() { - mWrapped.hideCurrentInputMethod(); - } - @Override public void setFinishTaskTransaction(int taskId, PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) { mPipTransaction = finishTransaction; @@ -406,6 +404,26 @@ public class RemoteTransitionCompat implements Parcelable { if (!mKeyguardLocked && mRecentsTask != null) { wct.restoreTransientOrder(mRecentsTask); } + } else if (toHome && mOpeningHome && mPausingTasks != null) { + // Special situaition where 3p launcher was changed during recents (this happens + // during tapltests...). Here we get both "return to home" AND "home opening". + // This is basically going home, but we have to restore recents order and also + // treat the home "pausing" task properly. + for (int i = mPausingTasks.size() - 1; i >= 0; --i) { + final TransitionInfo.Change change = mInfo.getChange(mPausingTasks.get(i)); + final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); + if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { + // Treat as opening (see above) + wct.reorder(mPausingTasks.get(i), true /* onTop */); + t.show(mInfo.getChange(mPausingTasks.get(i)).getLeash()); + } else { + // Treat as hiding (see below) + t.hide(mInfo.getChange(mPausingTasks.get(i)).getLeash()); + } + } + if (!mKeyguardLocked && mRecentsTask != null) { + wct.restoreTransientOrder(mRecentsTask); + } } else { for (int i = 0; i < mPausingTasks.size(); ++i) { if (!sendUserLeaveHint) { @@ -427,18 +445,14 @@ public class RemoteTransitionCompat implements Parcelable { mPipTransaction = null; } } - // Release surface references now. This is apparently to free GPU - // memory while doing quick operations (eg. during CTS). - for (int i = 0; i < mLeashMap.size(); ++i) { - if (mLeashMap.keyAt(i) == mLeashMap.valueAt(i)) continue; - t.remove(mLeashMap.valueAt(i)); - } try { mFinishCB.onTransitionFinished(wct.isEmpty() ? null : wct, t); } catch (RemoteException e) { Log.e("RemoteTransitionCompat", "Failed to call animation finish callback", e); t.apply(); } + // Only release the non-local created surface references. The animator is responsible + // for releasing the leashes created by local. for (int i = 0; i < mInfo.getChanges().size(); ++i) { mInfo.getChanges().get(i).getLeash().release(); } @@ -448,6 +462,7 @@ public class RemoteTransitionCompat implements Parcelable { mPausingTasks = null; mInfo = null; mOpeningLeashes = null; + mOpeningHome = false; mLeashMap = null; mTransition = null; } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java deleted file mode 100644 index 359d36939b31..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/UniversalSmartspaceUtils.java +++ /dev/null @@ -1,44 +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.systemui.shared.system; - -import android.content.Intent; -import android.os.Bundle; -import android.view.SurfaceView; - -/** Utility class that is shared between SysUI and Launcher for Universal Smartspace features. */ -public final class UniversalSmartspaceUtils { - public static final String ACTION_REQUEST_SMARTSPACE_VIEW = - "com.android.systemui.REQUEST_SMARTSPACE_VIEW"; - public static final String INTENT_BUNDLE_KEY = "bundle_key"; - - private static final String SYSUI_PACKAGE = "com.android.systemui"; - - /** Creates an intent to request that sysui draws the Smartspace to the SurfaceView. */ - public static Intent createRequestSmartspaceIntent(SurfaceView surfaceView) { - Intent intent = new Intent(ACTION_REQUEST_SMARTSPACE_VIEW); - - Bundle bundle = SurfaceViewRequestUtils.createSurfaceBundle(surfaceView); - return intent - .putExtra(INTENT_BUNDLE_KEY, bundle) - .setPackage(SYSUI_PACKAGE) - .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND); - } - - private UniversalSmartspaceUtils() {} -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index b894b10ff073..5577513f4c3c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -114,30 +114,6 @@ public class WindowManagerWrapper { } /** - * Sets if app requested fixed orientation should be ignored for given displayId. - */ - public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) { - try { - WindowManagerGlobal.getWindowManagerService().setIgnoreOrientationRequest( - displayId, ignoreOrientationRequest); - } catch (RemoteException e) { - Log.e(TAG, "Failed to setIgnoreOrientationRequest()", e); - } - } - - /** - * @return the stable insets for the primary display. - */ - public void getStableInsets(Rect outStableInsets) { - try { - WindowManagerGlobal.getWindowManagerService().getStableInsets(DEFAULT_DISPLAY, - outStableInsets); - } catch (RemoteException e) { - Log.e(TAG, "Failed to get stable insets", e); - } - } - - /** * Overrides a pending app transition. */ public void overridePendingAppTransitionMultiThumbFuture( @@ -153,16 +129,6 @@ public class WindowManagerWrapper { } } - public void overridePendingAppTransitionRemote( - RemoteAnimationAdapterCompat remoteAnimationAdapter, int displayId) { - try { - WindowManagerGlobal.getWindowManagerService().overridePendingAppTransitionRemote( - remoteAnimationAdapter.getWrapped(), displayId); - } catch (RemoteException e) { - Log.w(TAG, "Failed to override pending app transition (remote): ", e); - } - } - /** * Enable or disable haptic feedback on the navigation bar buttons. */ @@ -175,19 +141,6 @@ public class WindowManagerWrapper { } } - public void setRecentsVisibility(boolean visible) { - try { - WindowManagerGlobal.getWindowManagerService().setRecentsVisibility(visible); - } catch (RemoteException e) { - Log.w(TAG, "Failed to set recents visibility"); - } - } - - @Deprecated - public void setPipVisibility(final boolean visible) { - // To be removed - } - /** * @param displayId the id of display to check if there is a software navigation bar. * @@ -202,22 +155,6 @@ public class WindowManagerWrapper { } /** - * @return The side of the screen where navigation bar is positioned. - * @see #NAV_BAR_POS_RIGHT - * @see #NAV_BAR_POS_LEFT - * @see #NAV_BAR_POS_BOTTOM - * @see #NAV_BAR_POS_INVALID - */ - public int getNavBarPosition(int displayId) { - try { - return WindowManagerGlobal.getWindowManagerService().getNavBarPosition(displayId); - } catch (RemoteException e) { - Log.w(TAG, "Failed to get nav bar position"); - } - return NAV_BAR_POS_INVALID; - } - - /** * Mirrors a specified display. The SurfaceControl returned is the root of the mirrored * hierarchy. * diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index 12fa401d7fea..d32219a9817f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -336,6 +336,11 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> mKeyguardSecurityContainerController.onStartingToHide(); } + /** Called when bouncer visibility changes. */ + public void onBouncerVisibilityChanged(@View.Visibility int visibility) { + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(visibility); + } + public boolean hasDismissActions() { return mDismissAction != null || mCancelAction != null; } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt index db2b4ac2c669..58e0fb968204 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt @@ -58,7 +58,6 @@ data class KeyguardFaceListenModel( val keyguardAwake: Boolean, val keyguardGoingAway: Boolean, val listeningForFaceAssistant: Boolean, - val lockIconPressed: Boolean, val occludingAppRequestingFaceAuth: Boolean, val primaryUser: Boolean, val scanningAllowedByStrongAuth: Boolean, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 61e262440607..5ee659be6dd2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -17,6 +17,7 @@ package com.android.keyguard; import static android.app.StatusBarManager.SESSION_KEYGUARD; +import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC; import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS; @@ -32,11 +33,13 @@ import android.app.admin.DevicePolicyManager; import android.content.Intent; import android.content.res.ColorStateList; import android.content.res.Configuration; +import android.hardware.biometrics.BiometricSourceType; import android.metrics.LogMaker; import android.os.UserHandle; import android.util.Log; import android.util.Slog; import android.view.MotionEvent; +import android.view.View; import androidx.annotation.Nullable; @@ -55,6 +58,7 @@ import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; +import com.android.systemui.biometrics.SidefpsController; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; @@ -67,6 +71,8 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.ViewController; import com.android.systemui.util.settings.GlobalSettings; +import java.util.Optional; + import javax.inject.Inject; /** Controller for {@link KeyguardSecurityContainer} */ @@ -93,6 +99,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final GlobalSettings mGlobalSettings; private final FeatureFlags mFeatureFlags; private final SessionTracker mSessionTracker; + private final Optional<SidefpsController> mSidefpsController; private int mLastOrientation = Configuration.ORIENTATION_UNDEFINED; @@ -236,13 +243,27 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard reloadColors(); } }; + private boolean mBouncerVisible = false; private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onDevicePolicyManagerStateChanged() { - showPrimarySecurityScreen(false); - } - }; + @Override + public void onDevicePolicyManagerStateChanged() { + showPrimarySecurityScreen(false); + } + + @Override + public void onBiometricRunningStateChanged(boolean running, + BiometricSourceType biometricSourceType) { + if (biometricSourceType == FINGERPRINT) { + updateSideFpsVisibility(); + } + } + + @Override + public void onStrongAuthStateChanged(int userId) { + updateSideFpsVisibility(); + } + }; private KeyguardSecurityContainerController(KeyguardSecurityContainer view, AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory, @@ -260,7 +281,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard UserSwitcherController userSwitcherController, FeatureFlags featureFlags, GlobalSettings globalSettings, - SessionTracker sessionTracker) { + SessionTracker sessionTracker, + Optional<SidefpsController> sidefpsController) { super(view); mLockPatternUtils = lockPatternUtils; mUpdateMonitor = keyguardUpdateMonitor; @@ -280,6 +302,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mFeatureFlags = featureFlags; mGlobalSettings = globalSettings; mSessionTracker = sessionTracker; + mSidefpsController = sidefpsController; } @Override @@ -311,8 +334,23 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard getCurrentSecurityController().onPause(); } mView.onPause(); + // It might happen that onStartingToHide is not called when the device is locked while on + // bouncer. + setBouncerVisible(false); } + private void updateSideFpsVisibility() { + if (!mSidefpsController.isPresent()) { + return; + } + if (mBouncerVisible && mView.isSidedSecurityMode() + && mUpdateMonitor.isFingerprintDetectionRunning() + && !mUpdateMonitor.userNeedsStrongAuth()) { + mSidefpsController.get().show(); + } else { + mSidefpsController.get().hide(); + } + } /** * Shows the primary security screen for the user. This will be either the multi-selector @@ -397,6 +435,17 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard if (mCurrentSecurityMode != SecurityMode.None) { getCurrentSecurityController().onStartingToHide(); } + setBouncerVisible(false); + } + + /** Called when the bouncer changes visibility. */ + public void onBouncerVisibilityChanged(@View.Visibility int visibility) { + setBouncerVisible(visibility == View.VISIBLE); + } + + private void setBouncerVisible(boolean visible) { + mBouncerVisible = visible; + updateSideFpsVisibility(); } /** @@ -655,6 +704,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard private final FeatureFlags mFeatureFlags; private final UserSwitcherController mUserSwitcherController; private final SessionTracker mSessionTracker; + private final Optional<SidefpsController> mSidefpsController; @Inject Factory(KeyguardSecurityContainer view, @@ -673,7 +723,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard UserSwitcherController userSwitcherController, FeatureFlags featureFlags, GlobalSettings globalSettings, - SessionTracker sessionTracker) { + SessionTracker sessionTracker, + Optional<SidefpsController> sidefpsController) { mView = view; mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory; mLockPatternUtils = lockPatternUtils; @@ -690,6 +741,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mGlobalSettings = globalSettings; mUserSwitcherController = userSwitcherController; mSessionTracker = sessionTracker; + mSidefpsController = sidefpsController; } public KeyguardSecurityContainerController create( @@ -699,7 +751,8 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, mKeyguardStateController, securityCallback, mSecurityViewFlipperController, mConfigurationController, mFalsingCollector, mFalsingManager, - mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker); + mUserSwitcherController, mFeatureFlags, mGlobalSettings, mSessionTracker, + mSidefpsController); } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index ede62437e5a0..c19175742ce5 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -41,7 +41,6 @@ import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.AlarmManager; -import android.app.PendingIntent; import android.app.UserSwitchObserver; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; @@ -150,11 +149,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final boolean DEBUG_SPEW = false; private static final int BIOMETRIC_LOCKOUT_RESET_DELAY_MS = 600; - private static final String ACTION_FACE_UNLOCK_STARTED - = "com.android.facelock.FACE_UNLOCK_STARTED"; - private static final String ACTION_FACE_UNLOCK_STOPPED - = "com.android.facelock.FACE_UNLOCK_STOPPED"; - // Callback messages private static final int MSG_TIME_UPDATE = 301; private static final int MSG_BATTERY_UPDATE = 302; @@ -165,13 +159,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private static final int MSG_USER_SWITCHING = 310; private static final int MSG_KEYGUARD_RESET = 312; private static final int MSG_USER_SWITCH_COMPLETE = 314; - private static final int MSG_USER_INFO_CHANGED = 317; private static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318; private static final int MSG_STARTED_WAKING_UP = 319; private static final int MSG_FINISHED_GOING_TO_SLEEP = 320; private static final int MSG_STARTED_GOING_TO_SLEEP = 321; private static final int MSG_KEYGUARD_BOUNCER_CHANGED = 322; - private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 327; private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 328; private static final int MSG_AIRPLANE_MODE_CHANGED = 329; private static final int MSG_SERVICE_STATE_CHANGE = 330; @@ -250,7 +242,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final Context mContext; private final boolean mIsPrimaryUser; - private final boolean mIsAutomotive; private final AuthController mAuthController; private final StatusBarStateController mStatusBarStateController; private int mStatusBarState; @@ -328,8 +319,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private final LatencyTracker mLatencyTracker; private boolean mLogoutEnabled; private boolean mIsFaceEnrolled; - // If the user long pressed the lock icon, disabling face auth for the current session. - private boolean mLockIconPressed; private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID; private final Executor mBackgroundExecutor; private SensorPrivacyManager mSensorPrivacyManager; @@ -408,7 +397,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private SparseBooleanArray mUserHasTrust = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsUsuallyManaged = new SparseBooleanArray(); - private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray(); private Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<Integer, Intent>(); @VisibleForTesting @@ -1135,21 +1123,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private void handleFaceUnlockStateChanged(boolean running, int userId) { - Assert.isMainThread(); - mUserFaceUnlockRunning.put(userId, running); - for (int i = 0; i < mCallbacks.size(); i++) { - KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); - if (cb != null) { - cb.onFaceUnlockStateChanged(running, userId); - } - } - } - - public boolean isFaceUnlockRunning(int userId) { - return mUserFaceUnlockRunning.get(userId); - } - public boolean isFingerprintDetectionRunning() { return mFingerprintRunningState == BIOMETRIC_STATE_RUNNING; } @@ -1373,16 +1346,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - static class DisplayClientState { - public int clientGeneration; - public boolean clearing; - public PendingIntent intent; - public int playbackState; - public long playbackEventTime; - } - - private DisplayClientState mDisplayClientState = new DisplayClientState(); - @VisibleForTesting protected final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -1456,19 +1419,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final String action = intent.getAction(); if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(action)) { mHandler.sendEmptyMessage(MSG_TIME_UPDATE); - } else if (Intent.ACTION_USER_INFO_CHANGED.equals(action)) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_INFO_CHANGED, - intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()), 0)); - } else if (ACTION_FACE_UNLOCK_STARTED.equals(action)) { - Trace.beginSection( - "KeyguardUpdateMonitor.mBroadcastAllReceiver#onReceive " - + "ACTION_FACE_UNLOCK_STARTED"); - mHandler.sendMessage(mHandler.obtainMessage(MSG_FACE_UNLOCK_STATE_CHANGED, 1, - getSendingUserId())); - Trace.endSection(); - } else if (ACTION_FACE_UNLOCK_STOPPED.equals(action)) { - mHandler.sendMessage(mHandler.obtainMessage(MSG_FACE_UNLOCK_STATE_CHANGED, 0, - getSendingUserId())); } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED .equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED, @@ -1767,7 +1717,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab protected void handleStartedGoingToSleep(int arg1) { Assert.isMainThread(); - mLockIconPressed = false; clearBiometricRecognized(); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); @@ -1815,16 +1764,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - private void handleUserInfoChanged(int userId) { - Assert.isMainThread(); - for (int i = 0; i < mCallbacks.size(); i++) { - KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); - if (cb != null) { - cb.onUserInfoChanged(userId); - } - } - } - private void handleUserUnlocked(int userId) { Assert.isMainThread(); mUserIsUnlocked.put(userId, true); @@ -1942,9 +1881,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab case MSG_KEYGUARD_BOUNCER_CHANGED: handleKeyguardBouncerChanged(msg.arg1, msg.arg2); break; - case MSG_USER_INFO_CHANGED: - handleUserInfoChanged(msg.arg1); - break; case MSG_REPORT_EMERGENCY_CALL_ACTION: handleReportEmergencyCallAction(); break; @@ -1959,12 +1895,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab handleStartedWakingUp(); Trace.endSection(); break; - case MSG_FACE_UNLOCK_STATE_CHANGED: - Trace.beginSection( - "KeyguardUpdateMonitor#handler MSG_FACE_UNLOCK_STATE_CHANGED"); - handleFaceUnlockStateChanged(msg.arg1 != 0, msg.arg2); - Trace.endSection(); - break; case MSG_SIM_SUBSCRIPTION_INFO_CHANGED: handleSimSubscriptionInfoChanged(); break; @@ -2052,24 +1982,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab .getServiceStateForSubscriber(subId); mHandler.sendMessage( mHandler.obtainMessage(MSG_SERVICE_STATE_CHANGE, subId, 0, serviceState)); - - // Get initial state. Relying on Sticky behavior until API for getting info. - if (mBatteryStatus == null) { - Intent intent = mContext.registerReceiver( - null, - new IntentFilter(Intent.ACTION_BATTERY_CHANGED) - ); - if (intent != null && mBatteryStatus == null) { - mBroadcastReceiver.onReceive(mContext, intent); - } - } }); final IntentFilter allUserFilter = new IntentFilter(); - allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED); allUserFilter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); - allUserFilter.addAction(ACTION_FACE_UNLOCK_STARTED); - allUserFilter.addAction(ACTION_FACE_UNLOCK_STOPPED); allUserFilter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); allUserFilter.addAction(ACTION_USER_UNLOCKED); allUserFilter.addAction(ACTION_USER_STOPPED); @@ -2127,8 +2043,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mFaceManager.addLockoutResetCallback(mFaceLockoutResetCallback); } - mIsAutomotive = isAutomotive(); - TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener); mUserManager = context.getSystemService(UserManager.class); mIsPrimaryUser = mUserManager.isPrimaryUser(); @@ -2629,7 +2543,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab || mAuthController.isUdfpsFingerDown() || mUdfpsBouncerShowing) && !mSwitchingUser && !faceDisabledForUser && becauseCannotSkipBouncer - && !mKeyguardGoingAway && biometricEnabledForUser && !mLockIconPressed + && !mKeyguardGoingAway && biometricEnabledForUser && strongAuthAllowsScanning && mIsPrimaryUser && (!mSecureCameraLaunched || mOccludingAppRequestingFace) && !faceAuthenticated @@ -2652,7 +2566,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab awakeKeyguard, mKeyguardGoingAway, shouldListenForFaceAssistant, - mLockIconPressed, mOccludingAppRequestingFace, mIsPrimaryUser, strongAuthAllowsScanning, @@ -2697,18 +2610,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } - /** - * Whenever the lock icon is long pressed, disabling trust agents. - * This means that we cannot auth passively (face) until the user presses power. - */ - public void onLockIconPressed() { - mLockIconPressed = true; - final int userId = getCurrentUser(); - mUserFaceAuthenticated.put(userId, null); - updateFaceListeningState(BIOMETRIC_ACTION_UPDATE); - mStrongAuthTracker.onStrongAuthRequiredChanged(userId); - } - private void startListeningForFingerprint() { final int userId = getCurrentUser(); final boolean unlockPossible = isUnlockWithFingerprintPossible(userId); @@ -3179,20 +3080,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE); } - /** Notifies that the occluded state changed. */ - public void onKeyguardOccludedChanged(boolean occluded) { - Assert.isMainThread(); - if (DEBUG) { - Log.d(TAG, "onKeyguardOccludedChanged(" + occluded + ")"); - } - for (int i = 0; i < mCallbacks.size(); i++) { - KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); - if (cb != null) { - cb.onKeyguardOccludedChanged(occluded); - } - } - } - /** * Handle {@link #MSG_KEYGUARD_RESET} */ @@ -3347,10 +3234,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return false; } - private boolean isAutomotive() { - return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); - } - /** * Remove the given observer's callback. * @@ -3413,8 +3296,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab callback.onTimeChanged(); callback.onPhoneStateChanged(mPhoneState); callback.onRefreshCarrierInfo(); - callback.onClockVisibilityChanged(); - callback.onKeyguardOccludedChanged(mKeyguardOccluded); callback.onKeyguardVisibilityChangedRaw(mKeyguardIsVisible); callback.onTelephonyCapable(mTelephonyCapable); @@ -3591,10 +3472,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab || state == TelephonyManager.SIM_STATE_PERM_DISABLED); } - public DisplayClientState getCachedDisplayClientState() { - return mDisplayClientState; - } - // TODO: use these callbacks elsewhere in place of the existing notifyScreen*() // (KeyguardViewMediator, KeyguardHostView) public void dispatchStartedWakingUp() { @@ -3823,9 +3700,5 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab pw.println(" mNeedsSlowUnlockTransition=" + mNeedsSlowUnlockTransition); } mListenModels.print(pw); - - if (mIsAutomotive) { - pw.println(" Running on Automotive build"); - } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 051b81e484d8..99e0ce29a8c2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -80,12 +80,6 @@ public class KeyguardUpdateMonitorCallback { */ public void onKeyguardVisibilityChanged(boolean showing) { } - /** - * Called when the keyguard occluded state changes. - * @param occluded Indicates if the keyguard is now occluded. - */ - public void onKeyguardOccludedChanged(boolean occluded) { } - public void onKeyguardVisibilityChangedRaw(boolean showing) { final long now = SystemClock.elapsedRealtime(); if (showing == mShowing @@ -117,12 +111,6 @@ public class KeyguardUpdateMonitorCallback { public void onKeyguardDismissAnimationFinished() { } /** - * Called when visibility of lockscreen clock changes, such as when - * obscured by a widget. - */ - public void onClockVisibilityChanged() { } - - /** * Called when the device becomes provisioned */ public void onDeviceProvisioned() { } @@ -151,11 +139,6 @@ public class KeyguardUpdateMonitorCallback { public void onSimStateChanged(int subId, int slotId, int simState) { } /** - * Called when the user's info changed. - */ - public void onUserInfoChanged(int userId) { } - - /** * Called when a user got unlocked. */ public void onUserUnlocked() { } @@ -260,11 +243,6 @@ public class KeyguardUpdateMonitorCallback { BiometricSourceType biometricSourceType) { } /** - * Called when the state of face unlock changed. - */ - public void onFaceUnlockStateChanged(boolean running, int userId) { } - - /** * Called when biometric running state changed. */ public void onBiometricRunningStateChanged(boolean running, diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java index ca8728aecb4c..8293c74c5e75 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java @@ -23,10 +23,10 @@ import android.view.ViewRootImpl; import androidx.annotation.Nullable; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; /** diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index c1b2aba22b57..06e1828ef9f4 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -23,7 +23,6 @@ import static com.android.keyguard.LockIconView.ICON_LOCK; import static com.android.keyguard.LockIconView.ICON_UNLOCK; import static com.android.systemui.classifier.Classifier.LOCK_ICON; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; -import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInProgressOffset; import android.content.res.Configuration; import android.content.res.Resources; @@ -403,7 +402,6 @@ public class LockIconViewController extends ViewController<LockIconView> impleme float offsetY = MathUtils.lerp(0f, getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) - mMaxBurnInOffsetY, mInterpolatedDarkAmount); - float progress = MathUtils.lerp(0f, getBurnInProgressOffset(), mInterpolatedDarkAmount); mView.setTranslationX(offsetX); mView.setTranslationY(offsetY); @@ -652,7 +650,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme Process.myUid(), getContext().getOpPackageName(), UdfpsController.EFFECT_CLICK, - "lock-icon-device-entry", + "lock-screen-lock-icon-longpress", TOUCH_VIBRATION_ATTRIBUTES); mKeyguardViewController.showBouncer(/* scrim */ true); @@ -677,6 +675,12 @@ public class LockIconViewController extends ViewController<LockIconView> impleme } private boolean isActionable() { + if (mIsBouncerShowing) { + Log.v(TAG, "lock icon long-press ignored, bouncer already showing."); + // a long press gestures from AOD may have already triggered the bouncer to show, + // so this touch is no longer actionable + return false; + } return mUdfpsSupported || mShowUnlockIcon; } diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java index 013cdac94ab8..9a0bfc19f848 100644 --- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java +++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java @@ -53,8 +53,11 @@ import javax.inject.Inject; /** * Manages custom clock faces for AOD and lock screen. + * + * @deprecated Migrate to ClockRegistry */ @SysUISingleton +@Deprecated public final class ClockManager { private static final String TAG = "ClockOptsProvider"; diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java index b3c11584bcf8..49e97836b18b 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java @@ -16,6 +16,10 @@ package com.android.keyguard.dagger; +import static com.android.systemui.biometrics.SidefpsControllerKt.hasSideFpsSensor; + +import android.annotation.Nullable; +import android.hardware.fingerprint.FingerprintManager; import android.view.LayoutInflater; import android.view.ViewGroup; @@ -23,9 +27,14 @@ import com.android.keyguard.KeyguardHostView; import com.android.keyguard.KeyguardSecurityContainer; import com.android.keyguard.KeyguardSecurityViewFlipper; import com.android.systemui.R; +import com.android.systemui.biometrics.SidefpsController; import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.statusbar.phone.KeyguardBouncer; +import java.util.Optional; + +import javax.inject.Provider; + import dagger.Module; import dagger.Provides; @@ -60,4 +69,16 @@ public interface KeyguardBouncerModule { KeyguardSecurityContainer containerView) { return containerView.findViewById(R.id.view_flipper); } + + /** Provides {@link SidefpsController} if the device has the side fingerprint sensor. */ + @Provides + @KeyguardBouncerScope + static Optional<SidefpsController> providesOptionalSidefpsController( + @Nullable FingerprintManager fingerprintManager, + Provider<SidefpsController> sidefpsControllerProvider) { + if (!hasSideFpsSensor(fingerprintManager)) { + return Optional.empty(); + } + return Optional.of(sidefpsControllerProvider.get()); + } } diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java index 153da4b6695a..d01c98a934ff 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewComponent.java @@ -17,9 +17,9 @@ package com.android.keyguard.dagger; import com.android.keyguard.KeyguardStatusViewController; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; import dagger.BindsInstance; import dagger.Subcomponent; diff --git a/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java b/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java index b2d5c216dcd1..74d7a8b03b7d 100644 --- a/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java +++ b/packages/SystemUI/src/com/android/systemui/Gefingerpoken.java @@ -20,6 +20,13 @@ import android.view.MotionEvent; // ACHTUNG! public interface Gefingerpoken { - boolean onInterceptTouchEvent(MotionEvent ev); - boolean onTouchEvent(MotionEvent ev); + /** Called when a touch is being intercepted in a ViewGroup. */ + default boolean onInterceptTouchEvent(MotionEvent ev) { + return false; + } + + /** Called when a touch is being handled by a view. */ + default boolean onTouchEvent(MotionEvent ev) { + return false; + } } diff --git a/packages/SystemUI/src/com/android/systemui/Somnambulator.java b/packages/SystemUI/src/com/android/systemui/Somnambulator.java index 0dd6d9283223..25801cfe62b0 100644 --- a/packages/SystemUI/src/com/android/systemui/Somnambulator.java +++ b/packages/SystemUI/src/com/android/systemui/Somnambulator.java @@ -17,12 +17,15 @@ package com.android.systemui; import android.app.Activity; -import android.content.Intent; import android.service.dreams.Sandman; /** * A simple activity that launches a dream. * <p> + * + * This activity has been deprecated and no longer used. The system uses its presence to determine + * whether a dock app should be started on dock through intent resolution. + * * Note: This Activity is special. If this class is moved to another package or * renamed, be sure to update the component name in {@link Sandman}. * </p> @@ -34,27 +37,6 @@ public class Somnambulator extends Activity { @Override public void onStart() { super.onStart(); - - final Intent launchIntent = getIntent(); - final String action = launchIntent.getAction(); - if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) { - Intent shortcutIntent = new Intent(this, Somnambulator.class); - shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - | Intent.FLAG_ACTIVITY_NEW_TASK); - Intent resultIntent = new Intent(); - resultIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, - Intent.ShortcutIconResource.fromContext(this, R.mipmap.ic_launcher_dreams)); - resultIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); - resultIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.string.start_dreams)); - setResult(RESULT_OK, resultIntent); - } else { - boolean docked = launchIntent.hasCategory(Intent.CATEGORY_DESK_DOCK); - if (docked) { - Sandman.startDreamWhenDockedIfAppropriate(this); - } else { - Sandman.startDreamByUserRequest(this); - } - } finish(); } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java index 714d267bb07d..527ce127820e 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactory.java @@ -16,160 +16,22 @@ package com.android.systemui; -import android.app.Activity; -import android.app.Application; -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.ContentProvider; import android.content.Context; -import android.content.Intent; -import android.util.Log; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.app.AppComponentFactory; - -import com.android.systemui.dagger.ContextComponentHelper; -import com.android.systemui.dagger.SysUIComponent; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import javax.inject.Inject; /** - * Implementation of AppComponentFactory that injects into constructors. + * Starts up SystemUI using the AOSP {@link SystemUIInitializerImpl}. * - * This class sets up dependency injection when creating our application. + * This initializer relies on reflection to start everything up and should be considered deprecated. + * Instead, create your own {@link SystemUIAppComponentFactoryBase}, specify it in your + * AndroidManifest.xml and construct your own {@link SystemUIInitializer} directly. * - * Services support dependency injection into their constructors. - * - * ContentProviders support injection into member variables - _not_ constructors. + * @deprecated Define your own SystemUIAppComponentFactoryBase implementation and use that. This + * implementation may be changed or removed in future releases. */ -public class SystemUIAppComponentFactory extends AppComponentFactory { - - private static final String TAG = "AppComponentFactory"; - @Inject - public ContextComponentHelper mComponentHelper; - - public SystemUIAppComponentFactory() { - super(); - } - - @NonNull +@Deprecated +public class SystemUIAppComponentFactory extends SystemUIAppComponentFactoryBase { @Override - public Application instantiateApplicationCompat( - @NonNull ClassLoader cl, @NonNull String className) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - Application app = super.instantiateApplicationCompat(cl, className); - if (app instanceof ContextInitializer) { - ((ContextInitializer) app).setContextAvailableCallback( - context -> { - SystemUIFactory.createFromConfig(context); - SystemUIFactory.getInstance().getSysUIComponent().inject( - SystemUIAppComponentFactory.this); - } - ); - } - - return app; - } - - @NonNull - @Override - public ContentProvider instantiateProviderCompat( - @NonNull ClassLoader cl, @NonNull String className) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - - ContentProvider contentProvider = super.instantiateProviderCompat(cl, className); - if (contentProvider instanceof ContextInitializer) { - ((ContextInitializer) contentProvider).setContextAvailableCallback( - context -> { - SystemUIFactory.createFromConfig(context); - SysUIComponent rootComponent = - SystemUIFactory.getInstance().getSysUIComponent(); - try { - Method injectMethod = rootComponent.getClass() - .getMethod("inject", contentProvider.getClass()); - injectMethod.invoke(rootComponent, contentProvider); - } catch (NoSuchMethodException - | IllegalAccessException - | InvocationTargetException e) { - Log.w(TAG, "No injector for class: " + contentProvider.getClass(), e); - } - } - ); - } - - return contentProvider; - } - - @NonNull - @Override - public Activity instantiateActivityCompat(@NonNull ClassLoader cl, @NonNull String className, - @Nullable Intent intent) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - if (mComponentHelper == null) { - // This shouldn't happen, but is seen on occasion. - // Bug filed against framework to take a look: http://b/141008541 - SystemUIFactory.getInstance().getSysUIComponent().inject( - SystemUIAppComponentFactory.this); - } - Activity activity = mComponentHelper.resolveActivity(className); - if (activity != null) { - return activity; - } - return super.instantiateActivityCompat(cl, className, intent); - } - - @NonNull - @Override - public Service instantiateServiceCompat( - @NonNull ClassLoader cl, @NonNull String className, Intent intent) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - if (mComponentHelper == null) { - // This shouldn't happen, but does when a device is freshly formatted. - // Bug filed against framework to take a look: http://b/141008541 - SystemUIFactory.getInstance().getSysUIComponent().inject( - SystemUIAppComponentFactory.this); - } - Service service = mComponentHelper.resolveService(className); - if (service != null) { - return service; - } - return super.instantiateServiceCompat(cl, className, intent); - } - - @NonNull - @Override - public BroadcastReceiver instantiateReceiverCompat(@NonNull ClassLoader cl, - @NonNull String className, @Nullable Intent intent) - throws InstantiationException, IllegalAccessException, ClassNotFoundException { - if (mComponentHelper == null) { - // This shouldn't happen, but does when a device is freshly formatted. - // Bug filed against framework to take a look: http://b/141008541 - SystemUIFactory.getInstance().getSysUIComponent().inject( - SystemUIAppComponentFactory.this); - } - BroadcastReceiver receiver = mComponentHelper.resolveBroadcastReceiver(className); - if (receiver != null) { - return receiver; - } - - return super.instantiateReceiverCompat(cl, className, intent); - } - - /** - * A callback that receives a Context when one is ready. - */ - public interface ContextAvailableCallback { - void onContextAvailable(Context context); - } - - /** - * Implemented in classes that get started by the system before a context is available. - */ - public interface ContextInitializer { - void setContextAvailableCallback(ContextAvailableCallback callback); + protected SystemUIInitializer createSystemUIInitializer(Context context) { + return SystemUIInitializerFactory.createWithContext(context); } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt new file mode 100644 index 000000000000..12108b01ab28 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt @@ -0,0 +1,183 @@ +/* + * 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 + +import android.app.Activity +import android.app.Application +import android.app.Service +import android.content.BroadcastReceiver +import android.content.ContentProvider +import android.content.Context +import android.content.Intent +import android.util.Log +import androidx.core.app.AppComponentFactory +import com.android.systemui.dagger.ContextComponentHelper +import java.lang.reflect.InvocationTargetException +import java.util.concurrent.ExecutionException +import javax.inject.Inject + +/** + * Implementation of AppComponentFactory that injects into constructors. + * + * This class sets up dependency injection when creating our application. + * + * Activities, Services, and BroadcastReceivers support dependency injection into + * their constructors. + * + * ContentProviders support injection into member variables - _not_ constructors. + */ +abstract class SystemUIAppComponentFactoryBase : AppComponentFactory() { + companion object { + private const val TAG = "AppComponentFactory" + // Must be static due to http://b/141008541. + var systemUIInitializer: SystemUIInitializer? = null + } + + @set:Inject + lateinit var componentHelper: ContextComponentHelper + + /** + * Returns a new [SystemUIInitializer]. + * + * The returned implementation should be specific to your build. + */ + protected abstract fun createSystemUIInitializer(context: Context): SystemUIInitializer + + private fun createSystemUIInitializerInternal(context: Context): SystemUIInitializer { + return systemUIInitializer ?: run { + val initializer = createSystemUIInitializer(context.applicationContext) + try { + initializer.init(false) + } catch (exception: ExecutionException) { + throw RuntimeException("Failed to initialize SysUI", exception) + } catch (exception: InterruptedException) { + throw RuntimeException("Failed to initialize SysUI", exception) + } + initializer.sysUIComponent.inject( + this@SystemUIAppComponentFactoryBase + ) + + systemUIInitializer = initializer + return initializer + } + } + + override fun instantiateApplicationCompat(cl: ClassLoader, className: String): Application { + val app = super.instantiateApplicationCompat(cl, className) + if (app !is ContextInitializer) { + throw RuntimeException("App must implement ContextInitializer") + } else { + app.setContextAvailableCallback { context -> + createSystemUIInitializerInternal(context) + } + } + + return app + } + + override fun instantiateProviderCompat(cl: ClassLoader, className: String): ContentProvider { + val contentProvider = super.instantiateProviderCompat(cl, className) + if (contentProvider is ContextInitializer) { + contentProvider.setContextAvailableCallback { context -> + val initializer = createSystemUIInitializerInternal(context) + val rootComponent = initializer.sysUIComponent + try { + val injectMethod = rootComponent.javaClass + .getMethod("inject", contentProvider.javaClass) + injectMethod.invoke(rootComponent, contentProvider) + } catch (e: NoSuchMethodException) { + Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) + } catch (e: IllegalAccessException) { + Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) + } catch (e: InvocationTargetException) { + Log.w(TAG, "No injector for class: " + contentProvider.javaClass, e) + } + initializer + } + } + return contentProvider + } + + override fun instantiateActivityCompat( + cl: ClassLoader, + className: String, + intent: Intent? + ): Activity { + if (!this::componentHelper.isInitialized) { + // This shouldn't happen, but is seen on occasion. + // Bug filed against framework to take a look: http://b/141008541 + systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase) + } + return componentHelper.resolveActivity(className) + ?: super.instantiateActivityCompat(cl, className, intent) + } + + override fun instantiateServiceCompat( + cl: ClassLoader, + className: String, + intent: Intent? + ): Service { + if (!this::componentHelper.isInitialized) { + // This shouldn't happen, but does when a device is freshly formatted. + // Bug filed against framework to take a look: http://b/141008541 + systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase) + } + return componentHelper.resolveService(className) + ?: super.instantiateServiceCompat(cl, className, intent) + } + + override fun instantiateReceiverCompat( + cl: ClassLoader, + className: String, + intent: Intent? + ): BroadcastReceiver { + if (!this::componentHelper.isInitialized) { + // This shouldn't happen, but does when a device is freshly formatted. + // Bug filed against framework to take a look: http://b/141008541 + systemUIInitializer?.sysUIComponent?.inject(this@SystemUIAppComponentFactoryBase) + } + return componentHelper.resolveBroadcastReceiver(className) + ?: super.instantiateReceiverCompat(cl, className, intent) + } + + /** + * An Interface for classes that can be notified when an Application Context becomes available. + * + * An instance of this will be passed to implementers of [ContextInitializer]. + */ + fun interface ContextAvailableCallback { + /** Notifies when the Application Context is available. */ + fun onContextAvailable(context: Context): SystemUIInitializer + } + + /** + * Interface for classes that can be constructed by the system before a context is available. + * + * This is intended for [Application] and [ContentProvider] implementations that + * either may not have a Context until some point after construction or are themselves + * a [Context]. + * + * Implementers will be passed a [ContextAvailableCallback] that they should call as soon + * as an Application Context is ready. + */ + interface ContextInitializer { + /** + * Called to supply the [ContextAvailableCallback] that should be called when an + * Application [Context] is available. + */ + fun setContextAvailableCallback(callback: ContextAvailableCallback) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 6d3fd503dff6..9138b2346ab8 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -42,7 +42,6 @@ import android.view.SurfaceControl; import android.view.ThreadedRenderer; import com.android.internal.protolog.common.ProtoLog; -import com.android.systemui.dagger.ContextComponentHelper; import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dump.DumpManager; @@ -65,7 +64,6 @@ public class SystemUIApplication extends Application implements public static final String TAG = "SystemUIService"; private static final boolean DEBUG = false; - private ContextComponentHelper mComponentHelper; private BootCompleteCacheImpl mBootCompleteCache; private DumpManager mDumpManager; @@ -80,8 +78,8 @@ public class SystemUIApplication extends Application implements private CoreStartable[] mServices; private boolean mServicesStarted; private SystemUIAppComponentFactory.ContextAvailableCallback mContextAvailableCallback; - private GlobalRootComponent mRootComponent; private SysUIComponent mSysUIComponent; + private SystemUIInitializer mInitializer; public SystemUIApplication() { super(); @@ -90,6 +88,10 @@ public class SystemUIApplication extends Application implements ProtoLog.REQUIRE_PROTOLOGTOOL = false; } + protected GlobalRootComponent getRootComponent() { + return mInitializer.getRootComponent(); + } + @Override public void onCreate() { super.onCreate(); @@ -99,10 +101,8 @@ public class SystemUIApplication extends Application implements TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming", Trace.TRACE_TAG_APP); log.traceBegin("DependencyInjection"); - mContextAvailableCallback.onContextAvailable(this); - mRootComponent = SystemUIFactory.getInstance().getRootComponent(); - mSysUIComponent = SystemUIFactory.getInstance().getSysUIComponent(); - mComponentHelper = mSysUIComponent.getContextComponentHelper(); + mInitializer = mContextAvailableCallback.onContextAvailable(this); + mSysUIComponent = mInitializer.getSysUIComponent(); mBootCompleteCache = mSysUIComponent.provideBootCacheImpl(); log.traceEnd(); @@ -189,15 +189,14 @@ public class SystemUIApplication extends Application implements */ public void startServicesIfNeeded() { - final String vendorComponent = SystemUIFactory.getInstance() - .getVendorComponent(getResources()); + final String vendorComponent = mInitializer.getVendorComponent(getResources()); // Sort the startables so that we get a deterministic ordering. // TODO: make #start idempotent and require users of CoreStartable to call it. Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>( Comparator.comparing(Class::getName)); - sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponents()); - sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser()); + sortedStartables.putAll(mSysUIComponent.getStartables()); + sortedStartables.putAll(mSysUIComponent.getPerUserStartables()); startServicesIfNeeded( sortedStartables, "StartServices", vendorComponent); } @@ -212,7 +211,7 @@ public class SystemUIApplication extends Application implements // Sort the startables so that we get a deterministic ordering. Map<Class<?>, Provider<CoreStartable>> sortedStartables = new TreeMap<>( Comparator.comparing(Class::getName)); - sortedStartables.putAll(SystemUIFactory.getInstance().getStartableComponentsPerUser()); + sortedStartables.putAll(mSysUIComponent.getPerUserStartables()); startServicesIfNeeded( sortedStartables, "StartSecondaryServices", null); } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java index 2dade21caca7..24fcf15d7a11 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.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. @@ -11,97 +11,75 @@ * 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 + * limitations under the License. */ package com.android.systemui; -import android.app.ActivityThread; import android.content.Context; -import android.content.res.AssetManager; import android.content.res.Resources; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; -import com.android.internal.annotations.VisibleForTesting; -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.systemui.navigationbar.gestural.BackGestureTfClassifierProvider; -import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider; +import com.android.systemui.util.InitializationChecker; import com.android.wm.shell.dagger.WMShellConcurrencyModule; +import com.android.wm.shell.sysui.ShellInterface; import com.android.wm.shell.transition.ShellTransitions; -import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; - -import javax.inject.Provider; /** - * Class factory to provide customizable SystemUI components. + * Initializer that stands up SystemUI. + * + * Implementations should override {@link #getGlobalRootComponentBuilder()} to fill in their own + * Dagger root component. */ -public class SystemUIFactory { +public abstract class SystemUIInitializer { private static final String TAG = "SystemUIFactory"; - static SystemUIFactory mFactory; + private final Context mContext; + private GlobalRootComponent mRootComponent; private WMComponent mWMComponent; private SysUIComponent mSysUIComponent; - private boolean mInitializeComponents; + private InitializationChecker mInitializationChecker; - public static <T extends SystemUIFactory> T getInstance() { - return (T) mFactory; + public SystemUIInitializer(Context context) { + mContext = context; } - public static void createFromConfig(Context context) { - createFromConfig(context, false); - } - - @VisibleForTesting - public static void createFromConfig(Context context, boolean fromTest) { - if (mFactory != null) { - return; - } - - final String clsName = context.getString(R.string.config_systemUIFactoryComponent); - if (clsName == null || clsName.length() == 0) { - throw new RuntimeException("No SystemUIFactory component configured"); - } + protected abstract GlobalRootComponent.Builder getGlobalRootComponentBuilder(); - try { - Class<?> cls = null; - cls = context.getClassLoader().loadClass(clsName); - mFactory = (SystemUIFactory) cls.newInstance(); - mFactory.init(context, fromTest); - } catch (Throwable t) { - Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t); - throw new RuntimeException(t); - } - } - - @VisibleForTesting - static void cleanup() { - mFactory = null; + /** + * Prepares the SysUIComponent builder before it is built. + * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method + * @param wm the built WMComponent from the root component's getWMComponent() method + */ + protected SysUIComponent.Builder prepareSysUIComponentBuilder( + SysUIComponent.Builder sysUIBuilder, WMComponent wm) { + return sysUIBuilder; } - public SystemUIFactory() {} + /** + * Starts the initialization process. This stands up the Dagger graph. + */ + public void init(boolean fromTest) throws ExecutionException, InterruptedException { + mRootComponent = getGlobalRootComponentBuilder() + .context(mContext) + .instrumentationTest(fromTest) + .build(); - @VisibleForTesting - public void init(Context context, boolean fromTest) - throws ExecutionException, InterruptedException { - // Only initialize components for the main system ui process running as the primary user - mInitializeComponents = !fromTest - && android.os.Process.myUserHandle().isSystem() - && ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName()); - mRootComponent = buildGlobalRootComponent(context); + mInitializationChecker = mRootComponent.getInitializationChecker(); + boolean initializeComponents = mInitializationChecker.initializeComponents(); // Stand up WMComponent - setupWmComponent(context); - if (mInitializeComponents) { + setupWmComponent(mContext); + if (initializeComponents) { // Only initialize when not starting from tests since this currently initializes some // components that shouldn't be run in the test environment mWMComponent.init(); @@ -109,47 +87,41 @@ public class SystemUIFactory { // And finally, retrieve whatever SysUI needs from WMShell and build SysUI. SysUIComponent.Builder builder = mRootComponent.getSysUIComponent(); - if (mInitializeComponents) { + if (initializeComponents) { // Only initialize when not starting from tests since this currently initializes some // components that shouldn't be run in the test environment builder = prepareSysUIComponentBuilder(builder, mWMComponent) + .setShell(mWMComponent.getShell()) .setPip(mWMComponent.getPip()) .setSplitScreen(mWMComponent.getSplitScreen()) .setOneHanded(mWMComponent.getOneHanded()) .setBubbles(mWMComponent.getBubbles()) - .setHideDisplayCutout(mWMComponent.getHideDisplayCutout()) .setShellCommandHandler(mWMComponent.getShellCommandHandler()) .setTaskViewFactory(mWMComponent.getTaskViewFactory()) .setTransitions(mWMComponent.getTransitions()) .setStartingSurface(mWMComponent.getStartingSurface()) .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper()) - .setTaskSurfaceHelper(mWMComponent.getTaskSurfaceHelper()) .setRecentTasks(mWMComponent.getRecentTasks()) - .setCompatUI(mWMComponent.getCompatUI()) - .setDragAndDrop(mWMComponent.getDragAndDrop()) .setBackAnimation(mWMComponent.getBackAnimation()); } else { // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option // is separating this logic into newly creating SystemUITestsFactory. builder = prepareSysUIComponentBuilder(builder, mWMComponent) + .setShell(new ShellInterface() {}) .setPip(Optional.ofNullable(null)) .setSplitScreen(Optional.ofNullable(null)) .setOneHanded(Optional.ofNullable(null)) .setBubbles(Optional.ofNullable(null)) - .setHideDisplayCutout(Optional.ofNullable(null)) .setShellCommandHandler(Optional.ofNullable(null)) .setTaskViewFactory(Optional.ofNullable(null)) .setTransitions(new ShellTransitions() {}) .setDisplayAreaHelper(Optional.ofNullable(null)) .setStartingSurface(Optional.ofNullable(null)) - .setTaskSurfaceHelper(Optional.ofNullable(null)) .setRecentTasks(Optional.ofNullable(null)) - .setCompatUI(Optional.ofNullable(null)) - .setDragAndDrop(Optional.ofNullable(null)) .setBackAnimation(Optional.ofNullable(null)); } mSysUIComponent = builder.build(); - if (mInitializeComponents) { + if (initializeComponents) { mSysUIComponent.init(); } @@ -167,7 +139,8 @@ public class SystemUIFactory { */ private void setupWmComponent(Context context) { WMComponent.Builder wmBuilder = mRootComponent.getWMComponentBuilder(); - if (!mInitializeComponents || !WMShellConcurrencyModule.enableShellMainThread(context)) { + if (!mInitializationChecker.initializeComponents() + || !WMShellConcurrencyModule.enableShellMainThread(context)) { // If running under tests or shell thread is not enabled, we don't need anything special mWMComponent = wmBuilder.build(); return; @@ -189,26 +162,6 @@ public class SystemUIFactory { } } - /** - * Prepares the SysUIComponent builder before it is built. - * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method - * @param wm the built WMComponent from the root component's getWMComponent() method - */ - protected SysUIComponent.Builder prepareSysUIComponentBuilder( - SysUIComponent.Builder sysUIBuilder, WMComponent wm) { - return sysUIBuilder; - } - - protected GlobalRootComponent buildGlobalRootComponent(Context context) { - return DaggerGlobalRootComponent.builder() - .context(context) - .build(); - } - - protected boolean shouldInitializeComponents() { - return mInitializeComponents; - } - public GlobalRootComponent getRootComponent() { return mRootComponent; } @@ -222,42 +175,9 @@ public class SystemUIFactory { } /** - * Returns the list of {@link CoreStartable} components that should be started at startup. - */ - public Map<Class<?>, Provider<CoreStartable>> getStartableComponents() { - return mSysUIComponent.getStartables(); - } - - /** * Returns the list of additional system UI components that should be started. */ public String getVendorComponent(Resources resources) { return resources.getString(R.string.config_systemUIVendorServiceComponent); } - - /** - * Returns the list of {@link CoreStartable} components that should be started per user. - */ - public Map<Class<?>, Provider<CoreStartable>> getStartableComponentsPerUser() { - return mSysUIComponent.getPerUserStartables(); - } - - /** - * Creates an instance of ScreenshotNotificationSmartActionsProvider. - * This method is overridden in vendor specific implementation of Sys UI. - */ - public ScreenshotNotificationSmartActionsProvider - createScreenshotNotificationSmartActionsProvider( - Context context, Executor executor, Handler uiHandler) { - return new ScreenshotNotificationSmartActionsProvider(); - } - - /** - * Creates an instance of BackGestureTfClassifierProvider. - * This method is overridden in vendor specific implementation of Sys UI. - */ - public BackGestureTfClassifierProvider createBackGestureTfClassifierProvider( - AssetManager am, String modelName) { - return new BackGestureTfClassifierProvider(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt new file mode 100644 index 000000000000..b9454e8c3be8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt @@ -0,0 +1,72 @@ +/* + * 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.SuppressLint +import android.content.Context +import android.util.Log +import com.android.internal.annotations.VisibleForTesting +import com.android.systemui.util.Assert + +/** + * Factory to reflectively lookup a [SystemUIInitializer] to start SystemUI with. + */ +@Deprecated("Provide your own {@link SystemUIAppComponentFactoryBase} that doesn't need this.") +object SystemUIInitializerFactory { + private const val TAG = "SysUIInitializerFactory" + @SuppressLint("StaticFieldLeak") + private var initializer: SystemUIInitializer? = null + + /** + * Instantiate a [SystemUIInitializer] reflectively. + */ + @JvmStatic + fun createWithContext(context: Context): SystemUIInitializer { + return createFromConfig(context) + } + + /** + * Instantiate a [SystemUIInitializer] reflectively. + */ + @JvmStatic + private fun createFromConfig(context: Context): SystemUIInitializer { + Assert.isMainThread() + + return createFromConfigNoAssert(context) + } + + @JvmStatic + @VisibleForTesting + fun createFromConfigNoAssert(context: Context): SystemUIInitializer { + + return initializer ?: run { + val className = context.getString(R.string.config_systemUIFactoryComponent) + if (className.isEmpty()) { + throw RuntimeException("No SystemUIFactory component configured") + } + try { + val cls = context.classLoader.loadClass(className) + val constructor = cls.getConstructor(Context::class.java) + (constructor.newInstance(context) as SystemUIInitializer).apply { + initializer = this + } + } catch (t: Throwable) { + Log.w(TAG, "Error creating SystemUIInitializer component: $className", t) + throw t + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt new file mode 100644 index 000000000000..8920c928da09 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt @@ -0,0 +1,30 @@ +/* + * 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.content.Context +import com.android.systemui.dagger.DaggerGlobalRootComponent +import com.android.systemui.dagger.GlobalRootComponent + +/** + * {@link SystemUIInitializer} that stands up AOSP SystemUI. + */ +class SystemUIInitializerImpl(context: Context) : SystemUIInitializer(context) { + override fun getGlobalRootComponentBuilder(): GlobalRootComponent.Builder { + return DaggerGlobalRootComponent.builder() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java index d2703f5e73a2..aff0b1fe287c 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java @@ -353,6 +353,7 @@ public class AccessibilityFloatingMenuView extends FrameLayout } mIsShowing = false; + mDragAnimator.cancel(); mWindowManager.removeView(this); setOnApplyWindowInsetsListener(null); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index bc1c5f4baceb..84e1c3d4c8f0 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -90,6 +90,8 @@ public class AuthContainerView extends LinearLayout private static final int STATE_ANIMATING_OUT = 4; private static final int STATE_GONE = 5; + private static final float BACKGROUND_DIM_AMOUNT = 0.5f; + /** Shows biometric prompt dialog animation. */ private static final String SHOW = "show"; /** Dismiss biometric prompt dialog animation. */ @@ -247,13 +249,13 @@ public class AuthContainerView extends LinearLayout break; case AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN: mFailedModalities.clear(); - mConfig.mCallback.onTryAgainPressed(); + mConfig.mCallback.onTryAgainPressed(getRequestId()); break; case AuthBiometricView.Callback.ACTION_ERROR: animateAway(AuthDialogCallback.DISMISSED_ERROR); break; case AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL: - mConfig.mCallback.onDeviceCredentialPressed(); + mConfig.mCallback.onDeviceCredentialPressed(getRequestId()); mHandler.postDelayed(() -> { addCredentialView(false /* animatePanel */, true /* animateContents */); }, mConfig.mSkipAnimation ? 0 : AuthDialog.ANIMATE_CREDENTIAL_START_DELAY_MS); @@ -371,7 +373,7 @@ public class AuthContainerView extends LinearLayout void sendEarlyUserCanceled() { mConfig.mCallback.onSystemEvent( - BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL); + BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL, getRequestId()); } @Override @@ -754,6 +756,16 @@ public class AuthContainerView extends LinearLayout .setDuration(animateDuration) .setInterpolator(mLinearOutSlowIn) .setListener(getJankListener(this, DISMISS, animateDuration)) + .setUpdateListener(animation -> { + if (mWindowManager == null || getViewRootImpl() == null) { + Log.w(TAG, "skip updateViewLayout() for dim animation."); + return; + } + final WindowManager.LayoutParams lp = getViewRootImpl().mWindowAttributes; + lp.dimAmount = (1.0f - (Float) animation.getAnimatedValue()) + * BACKGROUND_DIM_AMOUNT; + mWindowManager.updateViewLayout(this, lp); + }) .withLayer() .start(); }); @@ -762,7 +774,8 @@ public class AuthContainerView extends LinearLayout private void sendPendingCallbackIfNotNull() { Log.d(TAG, "pendingCallback: " + mPendingCallbackReason); if (mPendingCallbackReason != null) { - mConfig.mCallback.onDismissed(mPendingCallbackReason, mCredentialAttestation); + mConfig.mCallback.onDismissed(mPendingCallbackReason, + mCredentialAttestation, getRequestId()); mPendingCallbackReason = null; } } @@ -792,7 +805,7 @@ public class AuthContainerView extends LinearLayout } mContainerState = STATE_SHOWING; if (mBiometricView != null) { - mConfig.mCallback.onDialogAnimatedIn(); + mConfig.mCallback.onDialogAnimatedIn(getRequestId()); mBiometricView.onDialogAnimatedIn(); } } @@ -800,7 +813,8 @@ public class AuthContainerView extends LinearLayout @VisibleForTesting static WindowManager.LayoutParams getLayoutParams(IBinder windowToken, CharSequence title) { final int windowFlags = WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED - | WindowManager.LayoutParams.FLAG_SECURE; + | WindowManager.LayoutParams.FLAG_SECURE + | WindowManager.LayoutParams.FLAG_DIM_BEHIND; final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, @@ -811,6 +825,7 @@ public class AuthContainerView extends LinearLayout lp.setFitInsetsTypes(lp.getFitInsetsTypes() & ~WindowInsets.Type.ime()); lp.setTitle("BiometricPrompt"); lp.accessibilityTitle = title; + lp.dimAmount = BACKGROUND_DIM_AMOUNT; lp.token = windowToken; return lp; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index a097c5e76149..47ff59cfc281 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -340,11 +340,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } @Override - public void onTryAgainPressed() { + public void onTryAgainPressed(long requestId) { if (mReceiver == null) { Log.e(TAG, "onTryAgainPressed: Receiver is null"); return; } + + if (requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "requestId doesn't match, skip onTryAgainPressed"); + return; + } + try { mReceiver.onTryAgainPressed(); } catch (RemoteException e) { @@ -353,11 +359,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } @Override - public void onDeviceCredentialPressed() { + public void onDeviceCredentialPressed(long requestId) { if (mReceiver == null) { Log.e(TAG, "onDeviceCredentialPressed: Receiver is null"); return; } + + if (requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "requestId doesn't match, skip onDeviceCredentialPressed"); + return; + } + try { mReceiver.onDeviceCredentialPressed(); } catch (RemoteException e) { @@ -366,11 +378,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } @Override - public void onSystemEvent(int event) { + public void onSystemEvent(int event, long requestId) { if (mReceiver == null) { Log.e(TAG, "onSystemEvent(" + event + "): Receiver is null"); return; } + + if (requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "requestId doesn't match, skip onSystemEvent"); + return; + } + try { mReceiver.onSystemEvent(event); } catch (RemoteException e) { @@ -379,12 +397,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } @Override - public void onDialogAnimatedIn() { + public void onDialogAnimatedIn(long requestId) { if (mReceiver == null) { Log.e(TAG, "onDialogAnimatedIn: Receiver is null"); return; } + if (requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "requestId doesn't match, skip onDialogAnimatedIn"); + return; + } + try { mReceiver.onDialogAnimatedIn(); } catch (RemoteException e) { @@ -393,7 +416,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } @Override - public void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation) { + public void onDismissed(@DismissedReason int reason, + @Nullable byte[] credentialAttestation, long requestId) { + + if (mCurrentDialog != null && requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "requestId doesn't match, skip onDismissed"); + return; + } + switch (reason) { case AuthDialogCallback.DISMISSED_USER_CANCELED: sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java index a7d2901b21c3..bbe461aaf6d9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java @@ -47,27 +47,28 @@ public interface AuthDialogCallback { * @param reason * @param credentialAttestation the HAT received from LockSettingsService upon verification */ - void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation); + void onDismissed(@DismissedReason int reason, + @Nullable byte[] credentialAttestation, long requestId); /** * Invoked when the "try again" button is clicked */ - void onTryAgainPressed(); + void onTryAgainPressed(long requestId); /** * Invoked when the "use password" button is clicked */ - void onDeviceCredentialPressed(); + void onDeviceCredentialPressed(long requestId); /** * See {@link android.hardware.biometrics.BiometricPrompt.Builder * #setReceiveSystemEvents(boolean)} * @param event */ - void onSystemEvent(int event); + void onSystemEvent(int event, long requestId); /** * Notifies when the dialog has finished animating. */ - void onDialogAnimatedIn(); + void onDialogAnimatedIn(long requestId); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt index 04e2dccda528..bbffb73b7503 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt @@ -34,16 +34,16 @@ import android.hardware.fingerprint.ISidefpsController import android.os.Handler import android.util.Log import android.util.RotationUtils -import android.view.View.AccessibilityDelegate -import android.view.accessibility.AccessibilityEvent import android.view.Display import android.view.Gravity import android.view.LayoutInflater import android.view.Surface import android.view.View +import android.view.View.AccessibilityDelegate import android.view.ViewPropertyAnimator import android.view.WindowInsets import android.view.WindowManager +import android.view.accessibility.AccessibilityEvent import androidx.annotation.RawRes import com.airbnb.lottie.LottieAnimationView import com.airbnb.lottie.LottieProperty @@ -70,13 +70,12 @@ class SidefpsController @Inject constructor( private val activityTaskManager: ActivityTaskManager, overviewProxyService: OverviewProxyService, displayManager: DisplayManager, - @Main mainExecutor: DelayableExecutor, + @Main private val mainExecutor: DelayableExecutor, @Main private val handler: Handler ) { @VisibleForTesting val sensorProps: FingerprintSensorPropertiesInternal = fingerprintManager - ?.sensorPropertiesInternal - ?.firstOrNull { it.isAnySidefpsType } + ?.sideFpsSensorProperties ?: throw IllegalStateException("no side fingerprint sensor") @VisibleForTesting @@ -135,25 +134,34 @@ class SidefpsController @Inject constructor( } init { - fingerprintManager?.setSidefpsController(object : ISidefpsController.Stub() { - override fun show( - sensorId: Int, - @BiometricOverlayConstants.ShowReason reason: Int - ) = if (reason.isReasonToShow(activityTaskManager)) doShow() else hide(sensorId) - - private fun doShow() = mainExecutor.execute { - if (overlayView == null) { - createOverlayForDisplay() - } else { - Log.v(TAG, "overlay already shown") - } - } + fingerprintManager?.setSidefpsController( + object : ISidefpsController.Stub() { + override fun show( + sensorId: Int, + @BiometricOverlayConstants.ShowReason reason: Int + ) = if (reason.isReasonToShow(activityTaskManager)) show() else hide() - override fun hide(sensorId: Int) = mainExecutor.execute { overlayView = null } - }) + override fun hide(sensorId: Int) = hide() + }) overviewProxyService.addCallback(overviewProxyListener) } + /** Shows the side fps overlay if not already shown. */ + fun show() { + mainExecutor.execute { + if (overlayView == null) { + createOverlayForDisplay() + } else { + Log.v(TAG, "overlay already shown") + } + } + } + + /** Hides the fps overlay if shown. */ + fun hide() { + mainExecutor.execute { overlayView = null } + } + private fun onOrientationChanged() { if (overlayView != null) { createOverlayForDisplay() @@ -266,6 +274,12 @@ class SidefpsController @Inject constructor( } } +private val FingerprintManager?.sideFpsSensorProperties: FingerprintSensorPropertiesInternal? + get() = this?.sensorPropertiesInternal?.firstOrNull { it.isAnySidefpsType } + +/** Returns [True] when the device has a side fingerprint sensor. */ +fun FingerprintManager?.hasSideFpsSensor(): Boolean = this?.sideFpsSensorProperties != null + @BiometricOverlayConstants.ShowReason private fun Int.isReasonToShow(activityTaskManager: ActivityTaskManager): Boolean = when (this) { REASON_AUTH_KEYGUARD -> false diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index e5564b7f1f26..cf50f7f8524b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -20,6 +20,7 @@ import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPR import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD; import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.systemui.classifier.Classifier.LOCK_ICON; import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION; import android.annotation.NonNull; @@ -167,11 +168,16 @@ public class UdfpsController implements DozeReceiver { private final Set<Callback> mCallbacks = new HashSet<>(); @VisibleForTesting - public static final VibrationAttributes VIBRATION_ATTRIBUTES = + public static final VibrationAttributes UDFPS_VIBRATION_ATTRIBUTES = new VibrationAttributes.Builder() // vibration will bypass battery saver mode: .setUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST) .build(); + @VisibleForTesting + public static final VibrationAttributes LOCK_ICON_VIBRATION_ATTRIBUTES = + new VibrationAttributes.Builder() + .setUsage(VibrationAttributes.USAGE_TOUCH) + .build(); // haptic to use for successful device entry public static final VibrationEffect EFFECT_CLICK = @@ -603,7 +609,7 @@ public class UdfpsController implements DozeReceiver { @NonNull SystemUIDialogManager dialogManager, @NonNull LatencyTracker latencyTracker, @NonNull ActivityLaunchAnimator activityLaunchAnimator, - @NonNull Optional<AlternateUdfpsTouchProvider> aternateTouchProvider, + @NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider, @BiometricsBackground Executor biometricsExecutor) { mContext = context; mExecution = execution; @@ -633,7 +639,7 @@ public class UdfpsController implements DozeReceiver { mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; mLatencyTracker = latencyTracker; mActivityLaunchAnimator = activityLaunchAnimator; - mAlternateTouchProvider = aternateTouchProvider.orElse(null); + mAlternateTouchProvider = alternateTouchProvider.orElse(null); mBiometricExecutor = biometricsExecutor; mOrientationListener = new BiometricDisplayListener( @@ -671,7 +677,7 @@ public class UdfpsController implements DozeReceiver { mContext.getOpPackageName(), EFFECT_CLICK, "udfps-onStart-click", - VIBRATION_ATTRIBUTES); + UDFPS_VIBRATION_ATTRIBUTES); } } @@ -748,7 +754,19 @@ public class UdfpsController implements DozeReceiver { } if (!mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) { + if (mFalsingManager.isFalseTouch(LOCK_ICON)) { + Log.v(TAG, "aod lock icon long-press rejected by the falsing manager."); + return; + } mKeyguardViewManager.showBouncer(true); + + // play the same haptic as the LockIconViewController longpress + mVibrator.vibrate( + Process.myUid(), + mContext.getOpPackageName(), + UdfpsController.EFFECT_CLICK, + "aod-lock-icon-longpress", + LOCK_ICON_VIBRATION_ATTRIBUTES); return; } diff --git a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt index 4986fe85af19..cccd3a482ef0 100644 --- a/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt @@ -17,27 +17,28 @@ package com.android.systemui.camera import android.app.ActivityManager -import android.app.ActivityManager.RunningTaskInfo import android.app.ActivityOptions -import android.app.ActivityTaskManager +import android.app.IActivityTaskManager import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.content.pm.ResolveInfo -import android.os.AsyncTask import android.os.RemoteException import android.os.UserHandle import android.util.Log import android.view.WindowManager +import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.ActivityIntentHelper -import com.android.systemui.camera.CameraIntents.Companion.isSecureCameraIntent +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.shared.system.ActivityManagerKt.isInForeground import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.PanelViewController import com.android.systemui.statusbar.policy.KeyguardStateController +import java.util.concurrent.Executor import javax.inject.Inject /** @@ -52,8 +53,10 @@ class CameraGestureHelper @Inject constructor( private val activityManager: ActivityManager, private val activityStarter: ActivityStarter, private val activityIntentHelper: ActivityIntentHelper, + private val activityTaskManager: IActivityTaskManager, private val cameraIntents: CameraIntentsWrapper, private val contentResolver: ContentResolver, + @Main private val uiExecutor: Executor, ) { /** * Whether the camera application can be launched for the camera launch gesture. @@ -63,15 +66,15 @@ class CameraGestureHelper @Inject constructor( return false } - val resolveInfo: ResolveInfo = packageManager.resolveActivityAsUser( + val resolveInfo: ResolveInfo? = packageManager.resolveActivityAsUser( getStartCameraIntent(), PackageManager.MATCH_DEFAULT_ONLY, KeyguardUpdateMonitor.getCurrentUser() ) - val resolvedPackage = resolveInfo.activityInfo?.packageName + val resolvedPackage = resolveInfo?.activityInfo?.packageName return (resolvedPackage != null && (statusBarState != StatusBarState.SHADE || - !isForegroundApp(resolvedPackage))) + !activityManager.isInForeground(resolvedPackage))) } /** @@ -85,8 +88,8 @@ class CameraGestureHelper @Inject constructor( val wouldLaunchResolverActivity = activityIntentHelper.wouldLaunchResolverActivity( intent, KeyguardUpdateMonitor.getCurrentUser() ) - if (isSecureCameraIntent(intent) && !wouldLaunchResolverActivity) { - AsyncTask.execute { + if (CameraIntents.isSecureCameraIntent(intent) && !wouldLaunchResolverActivity) { + uiExecutor.execute { // Normally an activity will set its requested rotation animation on its window. // However when launching an activity causes the orientation to change this is too // late. In these cases, the default animation is used. This doesn't look good for @@ -98,7 +101,7 @@ class CameraGestureHelper @Inject constructor( activityOptions.rotationAnimationHint = WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS try { - ActivityTaskManager.getService().startActivityAsUser( + activityTaskManager.startActivityAsUser( null, context.basePackageName, context.attributionTag, @@ -125,6 +128,13 @@ class CameraGestureHelper @Inject constructor( // launched from behind the lock-screen. activityStarter.startActivity(intent, false /* dismissShade */) } + + // Call this to make sure that the keyguard returns if the app that is being launched + // crashes after a timeout. + centralSurfaces.startLaunchTransitionTimeout() + // Call this to make sure the keyguard is ready to be dismissed once the next intent is + // handled by the OS (in our case it is the activity we started right above) + centralSurfaces.readyForKeyguardDone() } /** @@ -141,16 +151,8 @@ class CameraGestureHelper @Inject constructor( } } - /** - * Returns `true` if the application with the given package name is running in the foreground; - * `false` otherwise - */ - private fun isForegroundApp(packageName: String): Boolean { - val tasks: List<RunningTaskInfo> = activityManager.getRunningTasks(1) - return tasks.isNotEmpty() && packageName == tasks[0].topActivity.packageName - } - companion object { - private const val EXTRA_CAMERA_LAUNCH_SOURCE = "com.android.systemui.camera_launch_source" + @VisibleForTesting + const val EXTRA_CAMERA_LAUNCH_SOURCE = "com.android.systemui.camera_launch_source" } } diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java index 3f78f97ba563..1fa9ac574c7e 100644 --- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java +++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java @@ -74,6 +74,7 @@ public class EditTextActivity extends Activity } mEditText.setText(clip.getItemAt(0).getText()); mEditText.requestFocus(); + mEditText.setSelection(0); mSensitive = clip.getDescription().getExtras() != null && clip.getDescription().getExtras() .getBoolean(ClipDescription.EXTRA_IS_SENSITIVE); diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index bed553e6e4d6..50ce9d4ec0f4 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -20,13 +20,11 @@ import android.app.PendingIntent import android.app.backup.BackupManager import android.content.BroadcastReceiver import android.content.ComponentName -import android.content.ContentResolver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.database.ContentObserver import android.net.Uri -import android.os.Environment import android.os.UserHandle import android.service.controls.Control import android.service.controls.actions.ControlAction @@ -43,6 +41,7 @@ import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dump.DumpManager +import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_SEEDING_COMPLETED @@ -61,6 +60,7 @@ class ControlsControllerImpl @Inject constructor ( private val bindingController: ControlsBindingController, private val listingController: ControlsListingController, private val broadcastDispatcher: BroadcastDispatcher, + private val userFileManager: UserFileManager, optionalWrapper: Optional<ControlsFavoritePersistenceWrapper>, dumpManager: DumpManager, userTracker: UserTracker @@ -84,15 +84,12 @@ class ControlsControllerImpl @Inject constructor ( override val currentUserId get() = currentUser.identifier - private val contentResolver: ContentResolver - get() = context.contentResolver - private val persistenceWrapper: ControlsFavoritePersistenceWrapper @VisibleForTesting internal var auxiliaryPersistenceWrapper: AuxiliaryPersistenceWrapper init { - userStructure = UserStructure(context, currentUser) + userStructure = UserStructure(context, currentUser, userFileManager) persistenceWrapper = optionalWrapper.orElseGet { ControlsFavoritePersistenceWrapper( @@ -111,7 +108,7 @@ class ControlsControllerImpl @Inject constructor ( private fun setValuesForUser(newUser: UserHandle) { Log.d(TAG, "Changing to user: $newUser") currentUser = newUser - userStructure = UserStructure(context, currentUser) + userStructure = UserStructure(context, currentUser, userFileManager) persistenceWrapper.changeFileAndBackupManager( userStructure.file, BackupManager(userStructure.userContext) @@ -187,8 +184,11 @@ class ControlsControllerImpl @Inject constructor ( // When a component is uninstalled, allow seeding to happen again if the user // reinstalls the app - val prefs = userStructure.userContext.getSharedPreferences( - PREFS_CONTROLS_FILE, Context.MODE_PRIVATE) + val prefs = userFileManager.getSharedPreferences( + PREFS_CONTROLS_FILE, + Context.MODE_PRIVATE, + userTracker.userId + ) val completedSeedingPackageSet = prefs.getStringSet( PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>()) val servicePackageSet = serviceInfoSet.map { it.packageName } @@ -575,18 +575,12 @@ class ControlsControllerImpl @Inject constructor ( } } -class UserStructure(context: Context, user: UserHandle) { +class UserStructure(context: Context, user: UserHandle, userFileManager: UserFileManager) { val userContext = context.createContextAsUser(user, 0) - - val file = Environment.buildPath( - userContext.filesDir, - ControlsFavoritePersistenceWrapper.FILE_NAME - ) - - val auxiliaryFile = Environment.buildPath( - userContext.filesDir, - AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME - ) + val file = userFileManager.getFile(ControlsFavoritePersistenceWrapper.FILE_NAME, + user.identifier) + val auxiliaryFile = userFileManager.getFile(AuxiliaryPersistenceWrapper.AUXILIARY_FILE_NAME, + user.identifier) } /** diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index afc58ef70ee1..4096ed4283e5 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -42,6 +42,7 @@ import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; import android.content.pm.ShortcutManager; +import android.content.res.AssetManager; import android.content.res.Resources; import android.hardware.SensorManager; import android.hardware.SensorPrivacyManager; @@ -91,6 +92,7 @@ import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.util.LatencyTracker; import com.android.systemui.Prefs; +import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.TestHarness; @@ -405,6 +407,12 @@ public class FrameworkServicesModule { } @Provides + @Application + static AssetManager provideAssetManager(@Application Context context) { + return context.getAssets(); + } + + @Provides @Singleton static RoleManager provideRoleManager(Context context) { return context.getSystemService(RoleManager.class); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java index 4f55ba496b6b..9e33ee1faab3 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java @@ -18,6 +18,9 @@ package com.android.systemui.dagger; import android.content.Context; +import com.android.systemui.dagger.qualifiers.InstrumentationTest; +import com.android.systemui.util.InitializationChecker; + import javax.inject.Singleton; import dagger.BindsInstance; @@ -37,7 +40,8 @@ public interface GlobalRootComponent { interface Builder { @BindsInstance Builder context(Context context); - + @BindsInstance + Builder instrumentationTest(@InstrumentationTest boolean test); GlobalRootComponent build(); } @@ -50,4 +54,9 @@ public interface GlobalRootComponent { * Builder for a {@link SysUIComponent}, which makes it a subcomponent of this class. */ SysUIComponent.Builder getSysUIComponent(); + + /** + * Returns an {@link InitializationChecker}. + */ + InitializationChecker getInitializationChecker(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index 4e48a5261f6b..2c1463d285f6 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -35,6 +35,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; import com.android.systemui.media.dagger.MediaModule; +import com.android.systemui.navigationbar.gestural.GestureModule; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.EnhancedEstimates; @@ -43,6 +44,7 @@ import com.android.systemui.qs.dagger.QSModule; import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; +import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManagerImpl; @@ -94,9 +96,11 @@ import dagger.Provides; * SystemUI code that variants of SystemUI _must_ include to function correctly. */ @Module(includes = { + GestureModule.class, MediaModule.class, PowerModule.class, QSModule.class, + ReferenceScreenshotModule.class, StartCentralSurfacesModule.class, VolumeModule.class }) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 3a1b12955647..fd7680f463c0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -21,7 +21,7 @@ import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.CoreStartable; import com.android.systemui.Dependency; import com.android.systemui.InitController; -import com.android.systemui.SystemUIAppComponentFactory; +import com.android.systemui.SystemUIAppComponentFactoryBase; import com.android.systemui.dagger.qualifiers.PerUser; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardSliceProvider; @@ -41,16 +41,13 @@ import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.TaskViewFactory; 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.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; -import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper; +import com.android.wm.shell.sysui.ShellInterface; import com.android.wm.shell.transition.ShellTransitions; import java.util.Map; @@ -81,6 +78,9 @@ public interface SysUIComponent { @Subcomponent.Builder interface Builder { @BindsInstance + Builder setShell(ShellInterface s); + + @BindsInstance Builder setPip(Optional<Pip> p); @BindsInstance @@ -96,9 +96,6 @@ public interface SysUIComponent { Builder setTaskViewFactory(Optional<TaskViewFactory> t); @BindsInstance - Builder setHideDisplayCutout(Optional<HideDisplayCutout> h); - - @BindsInstance Builder setShellCommandHandler(Optional<ShellCommandHandler> shellDump); @BindsInstance @@ -111,18 +108,9 @@ public interface SysUIComponent { Builder setDisplayAreaHelper(Optional<DisplayAreaHelper> h); @BindsInstance - Builder setTaskSurfaceHelper(Optional<TaskSurfaceHelper> t); - - @BindsInstance Builder setRecentTasks(Optional<RecentTasks> r); @BindsInstance - Builder setCompatUI(Optional<CompatUI> s); - - @BindsInstance - Builder setDragAndDrop(Optional<DragAndDrop> d); - - @BindsInstance Builder setBackAnimation(Optional<BackAnimation> b); SysUIComponent build(); @@ -241,7 +229,7 @@ public interface SysUIComponent { /** * Member injection into the supplied argument. */ - void inject(SystemUIAppComponentFactory factory); + void inject(SystemUIAppComponentFactoryBase factory); /** * Member injection into the supplied argument. diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index a9f340854689..6db3e82a77b0 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -33,6 +33,7 @@ import com.android.systemui.log.SessionTracker import com.android.systemui.media.RingtonePlayer import com.android.systemui.power.PowerUI import com.android.systemui.recents.Recents +import com.android.systemui.settings.dagger.MultiUserUtilsModule import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.notification.InstantAppNotifier import com.android.systemui.statusbar.phone.KeyguardLiftController @@ -51,7 +52,7 @@ import dagger.multibindings.IntoMap /** * Collection of {@link CoreStartable}s that should be run on AOSP. */ -@Module +@Module(includes = [MultiUserUtilsModule::class]) abstract class SystemUICoreStartableModule { /** Inject into AuthController. */ @Binds @@ -205,4 +206,4 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(KeyguardLiftController::class) abstract fun bindKeyguardLiftController(sysui: KeyguardLiftController): CoreStartable -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index ba1e057716f8..fe9622250e67 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -26,7 +26,6 @@ import com.android.keyguard.clock.ClockModule; import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; -import com.android.systemui.SystemUIFactory; import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.assist.AssistModule; import com.android.systemui.biometrics.AlternateUdfpsTouchProvider; @@ -43,13 +42,15 @@ import com.android.systemui.flags.FlagsModule; import com.android.systemui.fragments.FragmentService; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.lowlightclock.LowLightClockController; +import com.android.systemui.media.dagger.MediaProjectionModule; import com.android.systemui.model.SysUiState; import com.android.systemui.navigationbar.NavigationBarComponent; +import com.android.systemui.people.PeopleModule; import com.android.systemui.plugins.BcSmartspaceDataPlugin; import com.android.systemui.privacy.PrivacyModule; import com.android.systemui.recents.Recents; import com.android.systemui.screenshot.dagger.ScreenshotModule; -import com.android.systemui.settings.dagger.SettingsModule; +import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.smartspace.dagger.SmartspaceModule; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -81,6 +82,7 @@ import com.android.systemui.unfold.SysUIUnfoldModule; import com.android.systemui.user.UserModule; import com.android.systemui.util.concurrency.SysUIConcurrencyModule; import com.android.systemui.util.dagger.UtilModule; +import com.android.systemui.util.kotlin.CoroutinesModule; import com.android.systemui.util.sensors.SensorModule; import com.android.systemui.util.settings.SettingsUtilModule; import com.android.systemui.util.time.SystemClock; @@ -89,6 +91,7 @@ import com.android.systemui.wallet.dagger.WalletModule; import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.dagger.DynamicOverride; +import com.android.wm.shell.sysui.ShellController; import java.util.Optional; import java.util.concurrent.Executor; @@ -113,19 +116,22 @@ import dagger.Provides; AssistModule.class, BiometricsModule.class, ClockModule.class, + CoroutinesModule.class, DreamModule.class, ControlsModule.class, DemoModeModule.class, FalsingModule.class, FlagsModule.class, LogModule.class, + MediaProjectionModule.class, PeopleHubModule.class, + PeopleModule.class, PluginModule.class, PrivacyModule.class, QsFrameTranslateModule.class, ScreenshotModule.class, SensorModule.class, - SettingsModule.class, + MultiUserUtilsModule.class, SettingsUtilModule.class, SmartRepliesInflationModule.class, SmartspaceModule.class, @@ -196,11 +202,6 @@ public abstract class SystemUIModule { @Binds abstract SystemClock bindSystemClock(SystemClockImpl systemClock); - @Provides - static SystemUIFactory getSystemUIFactory() { - return SystemUIFactory.getInstance(); - } - // TODO: This should provided by the WM component /** Provides Optional of BubbleManager */ @SysUISingleton @@ -210,7 +211,6 @@ public abstract class SystemUIModule { NotificationShadeWindowController notificationShadeWindowController, KeyguardStateController keyguardStateController, ShadeController shadeController, - ConfigurationController configurationController, @Nullable IStatusBarService statusBarService, INotificationManager notificationManager, NotificationVisibilityProvider visibilityProvider, @@ -228,7 +228,6 @@ public abstract class SystemUIModule { notificationShadeWindowController, keyguardStateController, shadeController, - configurationController, statusBarService, notificationManager, visibilityProvider, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index 1570a7ebc0c4..e4c0325d4d5a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -21,7 +21,7 @@ import android.os.HandlerThread; import androidx.annotation.Nullable; -import com.android.systemui.SystemUIFactory; +import com.android.systemui.SystemUIInitializerFactory; import com.android.systemui.tv.TvWMComponent; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.ShellInit; @@ -29,19 +29,16 @@ import com.android.wm.shell.TaskViewFactory; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.common.annotations.ShellMainThread; -import com.android.wm.shell.compatui.CompatUI; import com.android.wm.shell.dagger.TvWMShellModule; import com.android.wm.shell.dagger.WMShellModule; 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.onehanded.OneHanded; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.startingsurface.StartingSurface; -import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper; +import com.android.wm.shell.sysui.ShellInterface; import com.android.wm.shell.transition.ShellTransitions; import java.util.Optional; @@ -52,7 +49,7 @@ import dagger.Subcomponent; /** * Dagger Subcomponent for WindowManager. This class explicitly describes the interfaces exported * from the WM component into the SysUI component (in - * {@link SystemUIFactory#init(Context, boolean)}), and references the specific dependencies + * {@link SystemUIInitializerFactory#init(Context, boolean)}), and references the specific dependencies * provided by its particular device/form-factor SystemUI implementation. * * ie. {@link WMComponent} includes {@link WMShellModule} @@ -88,6 +85,9 @@ public interface WMComponent { Optional<ShellCommandHandler> getShellCommandHandler(); @WMSingleton + ShellInterface getShell(); + + @WMSingleton Optional<OneHanded> getOneHanded(); @WMSingleton @@ -100,9 +100,6 @@ public interface WMComponent { Optional<Bubbles> getBubbles(); @WMSingleton - Optional<HideDisplayCutout> getHideDisplayCutout(); - - @WMSingleton Optional<TaskViewFactory> getTaskViewFactory(); @WMSingleton @@ -115,17 +112,8 @@ public interface WMComponent { Optional<DisplayAreaHelper> getDisplayAreaHelper(); @WMSingleton - Optional<TaskSurfaceHelper> getTaskSurfaceHelper(); - - @WMSingleton Optional<RecentTasks> getRecentTasks(); @WMSingleton - Optional<CompatUI> getCompatUI(); - - @WMSingleton - Optional<DragAndDrop> getDragAndDrop(); - - @WMSingleton Optional<BackAnimation> getBackAnimation(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/InstrumentationTest.java index edeff6e37182..a803a39f114f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDrop.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/InstrumentationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 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,21 +14,21 @@ * limitations under the License. */ -package com.android.wm.shell.draganddrop; +package com.android.systemui.dagger.qualifiers; -import android.content.res.Configuration; +import static java.lang.annotation.RetentionPolicy.RUNTIME; -import com.android.wm.shell.common.annotations.ExternalThread; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; -/** - * Interface for telling DragAndDrop stuff. - */ -@ExternalThread -public interface DragAndDrop { +import javax.inject.Qualifier; - /** Called when the theme changes. */ - void onThemeChanged(); - /** Called when the configuration changes. */ - void onConfigChanged(Configuration newConfig); +/** + * An annotation for injecting whether or not we are running in a test environment. + */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface InstrumentationTest { } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 70e4fa3aa27f..7c816cec08f2 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -26,8 +26,6 @@ import android.os.SystemClock; import android.text.format.Formatter; import android.util.Log; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.dagger.DozeScope; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -44,8 +42,6 @@ import javax.inject.Inject; */ @DozeScope public class DozeUi implements DozeMachine.Part { - // if enabled, calls dozeTimeTick() whenever the time changes: - private static final boolean BURN_IN_TESTING_ENABLED = false; private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min private final Context mContext; private final DozeHost mHost; @@ -57,26 +53,13 @@ public class DozeUi implements DozeMachine.Part { private final DozeParameters mDozeParameters; private final DozeLog mDozeLog; private final StatusBarStateController mStatusBarStateController; - private final KeyguardUpdateMonitorCallback mKeyguardVisibilityCallback = - new KeyguardUpdateMonitorCallback() { - @Override - public void onTimeChanged() { - if (BURN_IN_TESTING_ENABLED && mStatusBarStateController.isDozing()) { - // update whenever the time changes for manual burn in testing - mHost.dozeTimeTick(); - - // Keep wakelock until a frame has been pushed. - mHandler.post(mWakeLock.wrap(() -> {})); - } - } - }; private long mLastTimeTickElapsed = 0; @Inject public DozeUi(Context context, AlarmManager alarmManager, WakeLock wakeLock, DozeHost host, @Main Handler handler, - DozeParameters params, KeyguardUpdateMonitor keyguardUpdateMonitor, + DozeParameters params, StatusBarStateController statusBarStateController, DozeLog dozeLog) { mContext = context; @@ -86,7 +69,6 @@ public class DozeUi implements DozeMachine.Part { mCanAnimateTransition = !params.getDisplayNeedsBlanking(); mDozeParameters = params; mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", handler); - keyguardUpdateMonitor.registerCallback(mKeyguardVisibilityCallback); mDozeLog = dozeLog; mStatusBarStateController = statusBarStateController; } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java index 59a17bad5069..a25257d6cf42 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java @@ -43,6 +43,8 @@ public class DreamOverlayStatusBarView extends ConstraintLayout { STATUS_ICON_NOTIFICATIONS, STATUS_ICON_WIFI_UNAVAILABLE, STATUS_ICON_ALARM_SET, + STATUS_ICON_CAMERA_DISABLED, + STATUS_ICON_MIC_DISABLED, STATUS_ICON_MIC_CAMERA_DISABLED, STATUS_ICON_PRIORITY_MODE_ON }) @@ -50,8 +52,10 @@ public class DreamOverlayStatusBarView extends ConstraintLayout { public static final int STATUS_ICON_NOTIFICATIONS = 0; public static final int STATUS_ICON_WIFI_UNAVAILABLE = 1; public static final int STATUS_ICON_ALARM_SET = 2; - public static final int STATUS_ICON_MIC_CAMERA_DISABLED = 3; - public static final int STATUS_ICON_PRIORITY_MODE_ON = 4; + public static final int STATUS_ICON_CAMERA_DISABLED = 3; + public static final int STATUS_ICON_MIC_DISABLED = 4; + public static final int STATUS_ICON_MIC_CAMERA_DISABLED = 5; + public static final int STATUS_ICON_PRIORITY_MODE_ON = 6; private final Map<Integer, View> mStatusIcons = new HashMap<>(); @@ -80,6 +84,10 @@ public class DreamOverlayStatusBarView extends ConstraintLayout { fetchStatusIconForResId(R.id.dream_overlay_wifi_status)); mStatusIcons.put(STATUS_ICON_ALARM_SET, fetchStatusIconForResId(R.id.dream_overlay_alarm_set)); + mStatusIcons.put(STATUS_ICON_CAMERA_DISABLED, + fetchStatusIconForResId(R.id.dream_overlay_camera_off)); + mStatusIcons.put(STATUS_ICON_MIC_DISABLED, + fetchStatusIconForResId(R.id.dream_overlay_mic_off)); mStatusIcons.put(STATUS_ICON_MIC_CAMERA_DISABLED, fetchStatusIconForResId(R.id.dream_overlay_camera_mic_off)); mStatusIcons.put(STATUS_ICON_NOTIFICATIONS, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java index 250313d0aae8..de7bf28c01d6 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java @@ -214,9 +214,17 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve .isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE); final boolean cameraBlocked = mSensorPrivacyController .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA); - showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, - micBlocked && cameraBlocked); + @DreamOverlayStatusBarView.StatusIconType int iconType = Resources.ID_NULL; + if (micBlocked && cameraBlocked) { + iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED; + } else if (!micBlocked && cameraBlocked) { + iconType = DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED; + } else if (micBlocked && !cameraBlocked) { + iconType = DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED; + } + if (iconType != Resources.ID_NULL) { + showIcon(iconType, true); + } } private String buildNotificationsContentDescription(int notificationCount) { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/AirQualityColorPicker.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/AirQualityColorPicker.java deleted file mode 100644 index 328753ff1539..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/AirQualityColorPicker.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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.dreams.complication; - -import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COLOR_DEFAULT; -import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COLOR_THRESHOLDS; -import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COLOR_VALUES; - -import android.util.Log; - -import androidx.annotation.ColorInt; - -import javax.inject.Inject; -import javax.inject.Named; - -final class AirQualityColorPicker { - private static final String TAG = "AirQualityColorPicker"; - private final int[] mThresholds; - private final int[] mColorValues; - private final int mDefaultColor; - - @Inject - AirQualityColorPicker(@Named(DREAM_AQI_COLOR_THRESHOLDS) int[] thresholds, - @Named(DREAM_AQI_COLOR_VALUES) int[] colorValues, - @Named(DREAM_AQI_COLOR_DEFAULT) @ColorInt int defaultColor) { - mThresholds = thresholds; - mColorValues = colorValues; - mDefaultColor = defaultColor; - } - - @ColorInt - int getColorForValue(String aqiString) { - int size = mThresholds.length; - if (mThresholds.length != mColorValues.length) { - size = Math.min(mThresholds.length, mColorValues.length); - Log.e(TAG, - "Threshold size (" - + mThresholds.length + ") does not match color value size (" - + mColorValues.length - + "). Taking the minimum, some values may be ignored."); - - } - try { - final int value = Integer.parseInt(aqiString.replaceAll("[^0-9]", "")); - for (int i = size - 1; i >= 0; i--) { - if (value > mThresholds[i]) { - return mColorValues[i]; - } - } - Log.e(TAG, "No matching AQI color for value: " + value); - } catch (NumberFormatException e) { - Log.e(TAG, "Could not read AQI value from:" + aqiString); - } - return mDefaultColor; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamAirQualityComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamAirQualityComplication.java deleted file mode 100644 index ba63303d1f2c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamAirQualityComplication.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * 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.dreams.complication; - -import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COMPLICATION_LAYOUT_PARAMS; -import static com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.DREAM_AQI_COMPLICATION_VIEW; -import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.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.GradientDrawable; -import android.graphics.drawable.ShapeDrawable; -import android.text.TextUtils; -import android.view.View; -import android.widget.TextView; - -import androidx.annotation.Nullable; - -import com.android.systemui.CoreStartable; -import com.android.systemui.dreams.DreamOverlayStateController; -import com.android.systemui.dreams.complication.dagger.DreamAirQualityComplicationComponent; -import com.android.systemui.dreams.smartspace.DreamSmartspaceController; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener; -import com.android.systemui.util.ViewController; - -import javax.inject.Inject; -import javax.inject.Named; - -/** - * Air quality complication which produces view holder responsible for showing AQI over dreams. - */ -public class DreamAirQualityComplication implements Complication { - // TODO(b/236024839): Move to SmartspaceTarget - public static final int FEATURE_AIR_QUALITY = 46; - - private final DreamAirQualityComplicationComponent.Factory mComponentFactory; - - @Inject - public DreamAirQualityComplication( - DreamAirQualityComplicationComponent.Factory componentFactory) { - mComponentFactory = componentFactory; - } - - @Override - public int getRequiredTypeAvailability() { - return COMPLICATION_TYPE_AIR_QUALITY; - } - - @Override - public ViewHolder createView(ComplicationViewModel model) { - return mComponentFactory.create().getViewHolder(); - } - - /** - * {@link CoreStartable} for registering {@link DreamAirQualityComplication} with SystemUI. - */ - public static class Registrant extends CoreStartable { - private final DreamOverlayStateController mDreamOverlayStateController; - private final DreamAirQualityComplication mComplication; - - /** - * Default constructor to register {@link DreamAirQualityComplication}. - */ - @Inject - public Registrant(Context context, - DreamOverlayStateController dreamOverlayStateController, - DreamAirQualityComplication complication) { - super(context); - mDreamOverlayStateController = dreamOverlayStateController; - mComplication = complication; - } - - @Override - public void start() { - // TODO(b/221500478): Only add complication once we have data to show. - mDreamOverlayStateController.addComplication(mComplication); - } - } - - /** - * ViewHolder to contain value/logic associated with the AQI complication view. - */ - public static class DreamAirQualityViewHolder implements ViewHolder { - private final TextView mView; - private final DreamAirQualityViewController mController; - private final ComplicationLayoutParams mLayoutParams; - - @Inject - DreamAirQualityViewHolder(@Named(DREAM_AQI_COMPLICATION_VIEW) TextView view, - DreamAirQualityViewController controller, - @Named(DREAM_AQI_COMPLICATION_LAYOUT_PARAMS) - ComplicationLayoutParams layoutParams) { - mView = view; - mLayoutParams = layoutParams; - mController = controller; - mController.init(); - } - - @Override - public View getView() { - return mView; - } - - @Override - public ComplicationLayoutParams getLayoutParams() { - return mLayoutParams; - } - } - - static class DreamAirQualityViewController extends ViewController<TextView> { - private final DreamSmartspaceController mSmartspaceController; - private final String mSmartspaceTrampolineComponent; - private final ActivityStarter mActivityStarter; - private final AirQualityColorPicker mAirQualityColorPicker; - - private final SmartspaceTargetListener mSmartspaceTargetListener = targets -> { - final SmartspaceTarget target = targets.stream() - .filter(t -> t instanceof SmartspaceTarget) - .map(t -> (SmartspaceTarget) t) - .filter(t -> t.getFeatureType() == FEATURE_AIR_QUALITY) - .findFirst() - .orElse(null); - updateView(target); - }; - - @Inject - DreamAirQualityViewController(@Named(DREAM_AQI_COMPLICATION_VIEW) TextView view, - DreamSmartspaceController smartspaceController, - @Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT) - String smartspaceTrampolineComponent, - ActivityStarter activityStarter, - AirQualityColorPicker airQualityColorPicker) { - super(view); - mSmartspaceController = smartspaceController; - mSmartspaceTrampolineComponent = smartspaceTrampolineComponent; - mActivityStarter = activityStarter; - mAirQualityColorPicker = airQualityColorPicker; - } - - @Override - protected void onViewAttached() { - mSmartspaceController.addUnfilteredListener(mSmartspaceTargetListener); - } - - @Override - protected void onViewDetached() { - mSmartspaceController.removeUnfilteredListener(mSmartspaceTargetListener); - } - - private void updateView(@Nullable SmartspaceTarget target) { - final SmartspaceAction headerAction = target == null ? null : target.getHeaderAction(); - if (headerAction == null || TextUtils.isEmpty(headerAction.getTitle())) { - mView.setVisibility(View.GONE); - return; - } - mView.setVisibility(View.VISIBLE); - - final String airQuality = headerAction.getTitle().toString(); - mView.setText(airQuality); - - final Drawable background = mView.getBackground().mutate(); - final int color = mAirQualityColorPicker.getColorForValue(airQuality); - - if (background instanceof ShapeDrawable) { - ((ShapeDrawable) background).getPaint().setColor(color); - } else if (background instanceof GradientDrawable) { - ((GradientDrawable) background).setColor(color); - } - mView.setBackground(background); - - final Intent intent = headerAction.getIntent(); - if (intent != null && intent.getComponent() != null - && intent.getComponent().getClassName().equals( - mSmartspaceTrampolineComponent)) { - mView.setOnClickListener(v -> { - mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0); - }); - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java deleted file mode 100644 index 4eae3b928c64..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java +++ /dev/null @@ -1,200 +0,0 @@ -/* - * 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.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.RegisteredComplicationsModule.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; -import android.widget.TextView; - -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.dreams.smartspace.DreamSmartspaceController; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener; -import com.android.systemui.util.ViewController; - -import javax.inject.Inject; -import javax.inject.Named; - -/** - * Weather Complication that produce Weather view holder. - */ -public class DreamWeatherComplication implements Complication { - DreamWeatherComplicationComponent.Factory mComponentFactory; - - /** - * Default constructor for {@link DreamWeatherComplication}. - */ - @Inject - public DreamWeatherComplication( - DreamWeatherComplicationComponent.Factory componentFactory) { - mComponentFactory = componentFactory; - } - - @Override - public int getRequiredTypeAvailability() { - return COMPLICATION_TYPE_WEATHER; - } - - /** - * Create {@link DreamWeatherViewHolder}. - */ - @Override - public ViewHolder createView(ComplicationViewModel model) { - return mComponentFactory.create().getViewHolder(); - } - - /** - * {@link CoreStartable} for registering {@link DreamWeatherComplication} with SystemUI. - */ - public static class Registrant extends CoreStartable { - private final DreamOverlayStateController mDreamOverlayStateController; - private final DreamWeatherComplication mComplication; - - /** - * Default constructor to register {@link DreamWeatherComplication}. - */ - @Inject - public Registrant(Context context, - DreamOverlayStateController dreamOverlayStateController, - DreamWeatherComplication dreamWeatherComplication) { - super(context); - mDreamOverlayStateController = dreamOverlayStateController; - mComplication = dreamWeatherComplication; - } - - @Override - public void start() { - mDreamOverlayStateController.addComplication(mComplication); - } - } - - /** - * ViewHolder to contain value/logic associated with a Weather Complication View. - */ - public static class DreamWeatherViewHolder implements ViewHolder { - private final TextView mView; - private final ComplicationLayoutParams mLayoutParams; - private final DreamWeatherViewController mViewController; - - @Inject - DreamWeatherViewHolder( - @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view, - DreamWeatherViewController controller, - @Named(DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS) - ComplicationLayoutParams layoutParams) { - mView = view; - mLayoutParams = layoutParams; - mViewController = controller; - mViewController.init(); - } - - @Override - public TextView getView() { - return mView; - } - - @Override - public ComplicationLayoutParams getLayoutParams() { - return mLayoutParams; - } - } - - /** - * ViewController to contain value/logic associated with a Weather Complication View. - */ - static class DreamWeatherViewController extends ViewController<TextView> { - private final DreamSmartspaceController 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, - DreamSmartspaceController smartspaceController - ) { - super(view); - mActivityStarter = activityStarter; - mSmartSpaceController = smartspaceController; - mSmartspaceTrampolineActivityComponent = smartspaceTrampoline; - } - - @Override - protected void onViewAttached() { - mSmartspaceTargetListener = targets -> targets.forEach( - t -> { - if (t instanceof SmartspaceTarget - && ((SmartspaceTarget) t).getFeatureType() - == SmartspaceTarget.FEATURE_WEATHER) { - final SmartspaceTarget target = (SmartspaceTarget) t; - final SmartspaceAction headerAction = target.getHeaderAction(); - if (headerAction == null || TextUtils.isEmpty( - headerAction.getTitle())) { - return; - } - - String temperature = headerAction.getTitle().toString(); - mView.setText(temperature); - final Icon icon = headerAction.getIcon(); - if (icon != null) { - final int iconSize = - getResources().getDimensionPixelSize( - R.dimen.smart_action_button_icon_size); - final Drawable iconDrawable = icon.loadDrawable(getContext()); - iconDrawable.setBounds(0, 0, iconSize, iconSize); - mView.setCompoundDrawables(iconDrawable, null, null, null); - mView.setCompoundDrawablePadding( - getResources().getDimensionPixelSize( - 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*/); - } - }); - } - }); - // We need to use an unfiltered listener here since weather is filtered from showing - // in the dream smartspace. - mSmartSpaceController.addUnfilteredListener(mSmartspaceTargetListener); - } - - @Override - protected void onViewDetached() { - mSmartSpaceController.removeUnfilteredListener(mSmartspaceTargetListener); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamAirQualityComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamAirQualityComplicationComponent.java deleted file mode 100644 index 112a1cea98a2..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamAirQualityComplicationComponent.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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.dreams.complication.dagger; - -import android.content.Context; -import android.content.res.Resources; -import android.view.LayoutInflater; -import android.view.ViewGroup; -import android.widget.TextView; - -import androidx.annotation.ColorInt; - -import com.android.systemui.R; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dreams.complication.ComplicationLayoutParams; -import com.android.systemui.dreams.complication.DreamAirQualityComplication; -import com.android.systemui.dreams.complication.DreamAirQualityComplication.DreamAirQualityViewHolder; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Objects; - -import javax.inject.Named; -import javax.inject.Scope; - -import dagger.Module; -import dagger.Provides; -import dagger.Subcomponent; - -/** - * Component responsible for generating dependencies for the {@link DreamAirQualityComplication}, - * such as the layout details. - */ -@Subcomponent(modules = { - DreamAirQualityComplicationComponent.DreamAirQualityComplicationModule.class, -}) -@DreamAirQualityComplicationComponent.DreamAirQualityComplicationScope -public interface DreamAirQualityComplicationComponent { - - @Documented - @Retention(RetentionPolicy.RUNTIME) - @Scope - @interface DreamAirQualityComplicationScope { - } - - /** - * Generates {@link DreamAirQualityComplicationComponent}. - */ - @Subcomponent.Factory - interface Factory { - DreamAirQualityComplicationComponent create(); - } - - /** - * Creates {@link DreamAirQualityViewHolder}. - */ - DreamAirQualityViewHolder getViewHolder(); - - /** - * Scoped values for {@link DreamAirQualityComplicationComponent}. - */ - @Module - interface DreamAirQualityComplicationModule { - String DREAM_AQI_COMPLICATION_VIEW = "aqi_complication_view"; - String DREAM_AQI_COMPLICATION_LAYOUT_PARAMS = "aqi_complication_layout_params"; - String DREAM_AQI_COLOR_THRESHOLDS = "aqi_color_thresholds"; - String DREAM_AQI_COLOR_VALUES = "aqi_color_values"; - String DREAM_AQI_COLOR_DEFAULT = "aqi_color_default"; - // Order weight of insert into parent container - int INSERT_ORDER_WEIGHT = 1; - - /** - * Provides the complication view. - */ - @Provides - @DreamAirQualityComplicationScope - @Named(DREAM_AQI_COMPLICATION_VIEW) - static TextView provideComplicationView(LayoutInflater layoutInflater) { - return Objects.requireNonNull((TextView) - layoutInflater.inflate(R.layout.dream_overlay_complication_aqi, - null, false), - "R.layout.dream_overlay_complication_aqi did not properly inflated"); - } - - /** - * Provides the layout parameters for the complication view. - */ - @Provides - @DreamAirQualityComplicationScope - @Named(DREAM_AQI_COMPLICATION_LAYOUT_PARAMS) - static ComplicationLayoutParams provideLayoutParams() { - return new ComplicationLayoutParams(0, - ViewGroup.LayoutParams.WRAP_CONTENT, - ComplicationLayoutParams.POSITION_BOTTOM - | ComplicationLayoutParams.POSITION_START, - ComplicationLayoutParams.DIRECTION_END, - INSERT_ORDER_WEIGHT, /* snapToGuide= */ true); - } - - @Provides - @DreamAirQualityComplicationScope - @Named(DREAM_AQI_COLOR_THRESHOLDS) - static int[] provideAqiColorThresholds(@Main Resources resources) { - return resources.getIntArray(R.array.config_dreamAqiThresholds); - } - - @Provides - @DreamAirQualityComplicationScope - @Named(DREAM_AQI_COLOR_VALUES) - static int[] provideAqiColorValues(@Main Resources resources) { - return resources.getIntArray(R.array.config_dreamAqiColorValues); - } - - @Provides - @DreamAirQualityComplicationScope - @Named(DREAM_AQI_COLOR_DEFAULT) - @ColorInt - static int provideDefaultAqiColor(Context context) { - return context.getColor(R.color.dream_overlay_aqi_unknown); - } - } -} 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 deleted file mode 100644 index 7ab3ad1a621a..000000000000 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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.dreams.complication.dagger; - - -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import android.view.LayoutInflater; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.android.internal.util.Preconditions; -import com.android.systemui.R; -import com.android.systemui.dreams.complication.ComplicationLayoutParams; -import com.android.systemui.dreams.complication.DreamWeatherComplication.DreamWeatherViewHolder; - -import java.lang.annotation.Documented; -import java.lang.annotation.Retention; - -import javax.inject.Named; -import javax.inject.Scope; - -import dagger.Module; -import dagger.Provides; -import dagger.Subcomponent; - -/** - * {@link DreamWeatherComplicationComponent} is responsible for generating dependencies surrounding - * the - * Clock Date {@link com.android.systemui.dreams.complication.Complication}, such as the layout - * details. - */ -@Subcomponent(modules = { - DreamWeatherComplicationComponent.DreamWeatherComplicationModule.class, -}) -@DreamWeatherComplicationComponent.DreamWeatherComplicationScope -public interface DreamWeatherComplicationComponent { - /** - * Creates {@link DreamWeatherViewHolder}. - */ - DreamWeatherViewHolder getViewHolder(); - - @Documented - @Retention(RUNTIME) - @Scope - @interface DreamWeatherComplicationScope { - } - - /** - * Generates {@link DreamWeatherComplicationComponent}. - */ - @Subcomponent.Factory - interface Factory { - DreamWeatherComplicationComponent create(); - } - - /** - * Scoped values for {@link DreamWeatherComplicationComponent}. - */ - @Module - interface DreamWeatherComplicationModule { - String DREAM_WEATHER_COMPLICATION_VIEW = "weather_complication_view"; - String DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS = - "weather_complication_layout_params"; - // Order weight of insert into parent container - int INSERT_ORDER_WEIGHT = 2; - - /** - * Provides the complication view. - */ - @Provides - @DreamWeatherComplicationScope - @Named(DREAM_WEATHER_COMPLICATION_VIEW) - static TextView provideComplicationView(LayoutInflater layoutInflater) { - return Preconditions.checkNotNull((TextView) - layoutInflater.inflate(R.layout.dream_overlay_complication_weather, - null, false), - "R.layout.dream_overlay_complication_weather did not properly inflated"); - } - - /** - * Provides the layout parameters for the complication view. - */ - @Provides - @DreamWeatherComplicationScope - @Named(DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS) - static ComplicationLayoutParams provideLayoutParams() { - return new ComplicationLayoutParams(0, - ViewGroup.LayoutParams.WRAP_CONTENT, - ComplicationLayoutParams.POSITION_BOTTOM - | ComplicationLayoutParams.POSITION_START, - ComplicationLayoutParams.DIRECTION_END, - INSERT_ORDER_WEIGHT, /* snapToGuide= */ true); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java index 98344aa7aae6..e45437dedd6e 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java @@ -16,15 +16,9 @@ package com.android.systemui.dreams.complication.dagger; -import android.content.Context; - -import com.android.systemui.R; import com.android.systemui.dagger.SystemUIBinder; -import javax.inject.Named; - import dagger.Module; -import dagger.Provides; /** * Module for all components with corresponding dream layer complications registered in @@ -33,20 +27,6 @@ import dagger.Provides; @Module(includes = { DreamClockDateComplicationModule.class, DreamClockTimeComplicationModule.class, - }, - subcomponents = { - DreamWeatherComplicationComponent.class, - DreamAirQualityComplicationComponent.class, }) public interface RegisteredComplicationsModule { - String SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT = "smartspace_trampoline_activity"; - - /** - * Provides the smartspace trampoline activity component. - */ - @Provides - @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/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java index 49b3908558d8..c5221cd9641b 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java +++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java @@ -32,7 +32,6 @@ import android.content.res.Resources; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; -import android.provider.DeviceConfig; import android.util.Log; import androidx.annotation.NonNull; @@ -197,7 +196,7 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { /** Specific override for Boolean flags that checks against the teamfood list.*/ private boolean readFlagValue(int id, boolean defaultValue) { - Boolean result = readFlagValueInternal(id, BooleanFlagSerializer.INSTANCE); + Boolean result = readBooleanFlagOverride(id); // Only check for teamfood if the default is false. if (!defaultValue && result == null && id != Flags.TEAMFOOD.getId()) { if (mAllFlags.containsKey(id) && mAllFlags.get(id).getTeamfood()) { @@ -208,6 +207,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { return result == null ? defaultValue : result; } + private Boolean readBooleanFlagOverride(int id) { + return readFlagValueInternal(id, BooleanFlagSerializer.INSTANCE); + } + @NonNull private <T> T readFlagValue(int id, @NonNull T defaultValue, FlagSerializer<T> serializer) { requireNonNull(defaultValue, "defaultValue"); @@ -407,11 +410,18 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { @Nullable private ParcelableFlag<?> toParcelableFlag(Flag<?> f) { if (f instanceof BooleanFlag) { - return new BooleanFlag(f.getId(), isEnabled((BooleanFlag) f), f.getTeamfood()); + return new BooleanFlag( + f.getId(), + isEnabled((BooleanFlag) f), + f.getTeamfood(), + readBooleanFlagOverride(f.getId()) != null); } if (f instanceof ResourceBooleanFlag) { return new BooleanFlag( - f.getId(), isEnabled((ResourceBooleanFlag) f), f.getTeamfood()); + f.getId(), + isEnabled((ResourceBooleanFlag) f), + f.getTeamfood(), + readBooleanFlagOverride(f.getId()) != null); } if (f instanceof DeviceConfigBooleanFlag) { return new BooleanFlag( @@ -420,7 +430,10 @@ public class FeatureFlagsDebug implements FeatureFlags, Dumpable { if (f instanceof SysPropBooleanFlag) { // TODO(b/223379190): Teamfood not supported for sysprop flags yet. return new BooleanFlag( - f.getId(), isEnabled((SysPropBooleanFlag) f), false); + f.getId(), + ((SysPropBooleanFlag) f).getDefault(), + false, + !mSystemProperties.get(((SysPropBooleanFlag) f).getName()).isEmpty()); } // TODO: add support for other flag types. diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 47a68bbbc8c0..46173b872817 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -84,6 +84,11 @@ public class Flags { public static final ResourceBooleanFlag FACE_SCANNING_ANIM = new ResourceBooleanFlag(205, R.bool.config_enableFaceScanningAnimation); + /** + * Whether the KeyguardBottomArea(View|Controller) should use the modern architecture or the old + * one. + */ + public static final BooleanFlag MODERN_BOTTOM_AREA = new BooleanFlag(206, false); /***************************************/ // 300 - power menu @@ -137,6 +142,9 @@ public class Flags { public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER = new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip); + public static final BooleanFlag STATUS_BAR_LETTERBOX_APPEARANCE = + new BooleanFlag(603, false); + /***************************************/ // 700 - dialer/calls public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP = @@ -163,7 +171,7 @@ 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); + public static final BooleanFlag DOCK_SETUP_ENABLED = new BooleanFlag(1001, true); // 1100 - windowing @@ -194,7 +202,8 @@ public class Flags { public static final SysPropBooleanFlag WM_ALWAYS_ENFORCE_PREDICTIVE_BACK = new SysPropBooleanFlag(1202, "persist.wm.debug.predictive_back_always_enforce", false); - public static final BooleanFlag NEW_BACK_AFFORDANCE = new BooleanFlag(1203, true); + public static final BooleanFlag NEW_BACK_AFFORDANCE = + new BooleanFlag(1203, false /* default */, true /* teamfood */); // Pay no attention to the reflection behind the curtain. // ========================== Curtain ========================== diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 3480ab68a5da..e913d2b86306 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -122,6 +122,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.dagger.KeyguardModule; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -131,7 +132,6 @@ import com.android.systemui.statusbar.phone.BiometricUnlockController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -556,15 +556,6 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, } @Override - public void onUserInfoChanged(int userId) { - } - - @Override - public void onClockVisibilityChanged() { - adjustStatusBarLocked(); - } - - @Override public void onDeviceProvisioned() { sendUserPresentBroadcast(); synchronized (KeyguardViewMediator.this) { @@ -1197,6 +1188,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, mSystemReady = true; doKeyguardLocked(null); mUpdateMonitor.registerCallback(mUpdateCallback); + adjustStatusBarLocked(); mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback); } // Most services aren't available until the system reaches the ready state, so we @@ -2707,7 +2699,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable, /** Hides the surface behind the keyguard by re-showing the keyguard/activity lock screen. */ public void hideSurfaceBehindKeyguard() { mSurfaceBehindRemoteAnimationRequested = false; - + mKeyguardStateController.notifyKeyguardGoingAway(false); if (mShowing) { setShowingLocked(true, true); } diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwner.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwner.kt new file mode 100644 index 000000000000..55c7ac9fb0cc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwner.kt @@ -0,0 +1,114 @@ +package com.android.systemui.lifecycle + +import android.view.View +import android.view.ViewTreeObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.LifecycleRegistry + +/** + * [LifecycleOwner] for Window-added Views. + * + * These are [View] instances that are added to a `Window` using the `WindowManager` API. + * + * This implementation goes to: + * * The <b>CREATED</b> `Lifecycle.State` when the view gets attached to the window but the window + * is not yet visible + * * The <b>STARTED</b> `Lifecycle.State` when the view is attached to the window and the window is + * visible + * * The <b>RESUMED</b> `Lifecycle.State` when the view is attached to the window and the window is + * visible and the window receives focus + * + * In table format: + * ``` + * | ----------------------------------------------------------------------------- | + * | View attached to window | Window visible | Window has focus | Lifecycle state | + * | ----------------------------------------------------------------------------- | + * | not attached | Any | INITIALIZED | + * | ----------------------------------------------------------------------------- | + * | | not visible | Any | CREATED | + * | ----------------------------------------------------- | + * | attached | | not focused | STARTED | + * | | is visible |----------------------------------- | + * | | | has focus | RESUMED | + * | ----------------------------------------------------------------------------- | + * ``` + * ### Notes + * * [dispose] must be invoked when the [LifecycleOwner] is done and won't be reused + * * It is always better for [LifecycleOwner] implementations to be more explicit than just + * listening to the state of the `Window`. E.g. if the code that added the `View` to the `Window` + * already has access to the correct state to know when that `View` should become visible and when + * it is ready to receive interaction from the user then it already knows when to move to `STARTED` + * and `RESUMED`, respectively. In that case, it's better to implement your own `LifecycleOwner` + * instead of relying on the `Window` callbacks. + */ +class WindowAddedViewLifecycleOwner +@JvmOverloads +constructor( + private val view: View, + registryFactory: (LifecycleOwner) -> LifecycleRegistry = { LifecycleRegistry(it) }, +) : LifecycleOwner { + + private val windowAttachListener = + object : ViewTreeObserver.OnWindowAttachListener { + override fun onWindowAttached() { + updateCurrentState() + } + + override fun onWindowDetached() { + updateCurrentState() + } + } + private val windowFocusListener = + ViewTreeObserver.OnWindowFocusChangeListener { updateCurrentState() } + private val windowVisibilityListener = + ViewTreeObserver.OnWindowVisibilityChangeListener { updateCurrentState() } + + private val registry = registryFactory(this) + + init { + setCurrentState(Lifecycle.State.INITIALIZED) + + with(view.viewTreeObserver) { + addOnWindowAttachListener(windowAttachListener) + addOnWindowVisibilityChangeListener(windowVisibilityListener) + addOnWindowFocusChangeListener(windowFocusListener) + } + + updateCurrentState() + } + + override fun getLifecycle(): Lifecycle { + return registry + } + + /** + * Disposes of this [LifecycleOwner], performing proper clean-up. + * + * <p>Invoke this when the instance is finished and won't be reused. + */ + fun dispose() { + with(view.viewTreeObserver) { + removeOnWindowAttachListener(windowAttachListener) + removeOnWindowVisibilityChangeListener(windowVisibilityListener) + removeOnWindowFocusChangeListener(windowFocusListener) + } + } + + private fun updateCurrentState() { + val state = + when { + !view.isAttachedToWindow -> Lifecycle.State.INITIALIZED + view.windowVisibility != View.VISIBLE -> Lifecycle.State.CREATED + !view.hasWindowFocus() -> Lifecycle.State.STARTED + else -> Lifecycle.State.RESUMED + } + setCurrentState(state) + } + + private fun setCurrentState(state: Lifecycle.State) { + if (registry.currentState != state) { + registry.currentState = state + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 1ed65b31dbd0..012d76651b23 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -682,9 +682,22 @@ public class MediaControlPanel { Drawable artwork; boolean isArtworkBound; Icon artworkIcon = data.getArtwork(); + WallpaperColors wallpaperColors = null; if (artworkIcon != null) { - WallpaperColors wallpaperColors = WallpaperColors - .fromBitmap(artworkIcon.getBitmap()); + if (artworkIcon.getType() == Icon.TYPE_BITMAP + || artworkIcon.getType() == Icon.TYPE_ADAPTIVE_BITMAP) { + // Avoids extra processing if this is already a valid bitmap + wallpaperColors = WallpaperColors + .fromBitmap(artworkIcon.getBitmap()); + } else { + Drawable artworkDrawable = artworkIcon.loadDrawable(mContext); + if (artworkDrawable != null) { + wallpaperColors = WallpaperColors + .fromDrawable(artworkIcon.loadDrawable(mContext)); + } + } + } + if (wallpaperColors != null) { mutableColorScheme = new ColorScheme(wallpaperColors, true, Style.CONTENT); Drawable albumArt = getScaledBackground(artworkIcon, width, height); GradientDrawable gradient = (GradientDrawable) mContext @@ -1005,16 +1018,13 @@ public class MediaControlPanel { private void bindScrubbingTime(MediaData data) { ConstraintSet expandedSet = mMediaViewController.getExpandedLayout(); - ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout(); int elapsedTimeId = mMediaViewHolder.getScrubbingElapsedTimeView().getId(); int totalTimeId = mMediaViewHolder.getScrubbingTotalTimeView().getId(); boolean visible = scrubbingTimeViewsEnabled(data.getSemanticActions()) && mIsScrubbing; setVisibleAndAlpha(expandedSet, elapsedTimeId, visible); setVisibleAndAlpha(expandedSet, totalTimeId, visible); - // Never show in collapsed - setVisibleAndAlpha(collapsedSet, elapsedTimeId, false); - setVisibleAndAlpha(collapsedSet, totalTimeId, false); + // Collapsed view is always GONE as set in XML, so doesn't need to be updated dynamically } private boolean scrubbingTimeViewsEnabled(@Nullable MediaButton semanticActions) { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt index 2f732de50ea1..458ed4059b3b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt @@ -144,8 +144,7 @@ class MediaHierarchyManager @Inject constructor( animatedFraction) // When crossfading, let's keep the bounds at the right location during fading boundsProgress = if (animationCrossFadeProgress < 0.5f) 0.0f else 1.0f - currentAlpha = calculateAlphaFromCrossFade(animationCrossFadeProgress, - instantlyShowAtEnd = false) + currentAlpha = calculateAlphaFromCrossFade(animationCrossFadeProgress) } else { // If we're not crossfading, let's interpolate from the start alpha to 1.0f currentAlpha = MathUtils.lerp(animationStartAlpha, 1.0f, animatedFraction) @@ -276,7 +275,7 @@ class MediaHierarchyManager @Inject constructor( if (value >= 0) { updateTargetState() // Setting the alpha directly, as the below call will use it to update the alpha - carouselAlpha = calculateAlphaFromCrossFade(field, instantlyShowAtEnd = true) + carouselAlpha = calculateAlphaFromCrossFade(field) applyTargetStateIfNotAnimating() } } @@ -414,18 +413,10 @@ class MediaHierarchyManager @Inject constructor( * @param crossFadeProgress The current cross fade progress. 0.5f means it's just switching * between the start and the end location and the content is fully faded, while 0.75f means * that we're halfway faded in again in the target state. - * - * @param instantlyShowAtEnd should the view be instantly shown at the end. This is needed - * to avoid fadinging in when the target was hidden anyway. */ - private fun calculateAlphaFromCrossFade( - crossFadeProgress: Float, - instantlyShowAtEnd: Boolean - ): Float { + private fun calculateAlphaFromCrossFade(crossFadeProgress: Float): Float { if (crossFadeProgress <= 0.5f) { return 1.0f - crossFadeProgress / 0.5f - } else if (instantlyShowAtEnd) { - return 1.0f } else { return (crossFadeProgress - 0.5f) / 0.5f } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt new file mode 100644 index 000000000000..6802da347c93 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt @@ -0,0 +1,119 @@ +/* + * 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.app.ActivityOptions +import android.content.Intent +import android.media.projection.IMediaProjection +import android.media.projection.MediaProjectionManager.EXTRA_MEDIA_PROJECTION +import android.os.Binder +import android.os.Bundle +import android.os.IBinder +import android.view.View +import com.android.internal.app.ChooserActivity +import com.android.internal.app.chooser.NotSelectableTargetInfo +import com.android.internal.app.chooser.TargetInfo +import com.android.systemui.util.AsyncActivityLauncher +import com.android.systemui.R; +import javax.inject.Inject + +class MediaProjectionAppSelectorActivity @Inject constructor( + private val activityLauncher: AsyncActivityLauncher +) : ChooserActivity() { + + public override fun onCreate(bundle: Bundle?) { + val queryIntent = Intent(Intent.ACTION_MAIN) + .addCategory(Intent.CATEGORY_LAUNCHER) + intent.putExtra(Intent.EXTRA_INTENT, queryIntent) + + // TODO(b/235465652) Use resource lexeme + intent.putExtra(Intent.EXTRA_TITLE, "Record or cast an app") + + super.onCreate(bundle) + + // TODO(b/235465652) we should update VisD of the title and add an icon + findViewById<View>(R.id.title)?.visibility = View.VISIBLE + } + + override fun appliedThemeResId(): Int = + R.style.Theme_SystemUI_MediaProjectionAppSelector + + override fun startSelected(which: Int, always: Boolean, filtered: Boolean) { + val currentListAdapter = mChooserMultiProfilePagerAdapter.activeListAdapter + val targetInfo = currentListAdapter.targetInfoForPosition(which, filtered) ?: return + if (targetInfo is NotSelectableTargetInfo) return + + val intent = createIntent(targetInfo) + + val launchToken: IBinder = Binder("media_projection_launch_token") + val activityOptions = ActivityOptions.makeBasic() + activityOptions.launchCookie = launchToken + + val userHandle = mMultiProfilePagerAdapter.activeListAdapter.userHandle + + // Launch activity asynchronously and wait for the result, launching of an activity + // is typically very fast, so we don't show any loaders. + // We wait for the activity to be launched to make sure that the window of the activity + // is created and ready to be captured. + val activityStarted = activityLauncher + .startActivityAsUser(intent, userHandle, activityOptions.toBundle()) { + onTargetActivityLaunched(launchToken) + } + + // Rely on the ActivityManager to pop up a dialog regarding app suspension + // and return false if suspended + if (!targetInfo.isSuspended && activityStarted) { + // TODO(b/222078415) track activity launch + } + } + + private fun createIntent(target: TargetInfo): Intent { + val intent = Intent(target.resolvedIntent) + + // Launch the app in a new task, so it won't be in the host's app task + intent.flags = intent.flags or Intent.FLAG_ACTIVITY_NEW_TASK + + // Remove activity forward result flag as this activity will + // return the media projection session + intent.flags = intent.flags and Intent.FLAG_ACTIVITY_FORWARD_RESULT.inv() + + return intent + } + + override fun onDestroy() { + activityLauncher.destroy() + super.onDestroy() + } + + override fun onActivityStarted(cti: TargetInfo) { + // do nothing + } + + private fun onTargetActivityLaunched(launchToken: IBinder) { + val mediaProjectionBinder = intent.getIBinderExtra(EXTRA_MEDIA_PROJECTION) + val projection = IMediaProjection.Stub.asInterface(mediaProjectionBinder) + + projection.launchCookie = launchToken + + val intent = Intent() + intent.putExtra(EXTRA_MEDIA_PROJECTION, projection.asBinder()) + setResult(RESULT_OK, intent) + setForceSendResultForMediaProjection() + finish() + } + + override fun shouldGetOnlyDefaultActivities() = false +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java index 38604091c409..397bffcaa64c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java @@ -40,7 +40,10 @@ import android.text.style.StyleSpan; import android.util.Log; import android.view.Window; +import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.util.Utils; @@ -53,6 +56,7 @@ public class MediaProjectionPermissionActivity extends Activity private String mPackageName; private int mUid; private IMediaProjectionManager mService; + private FeatureFlags mFeatureFlags; private AlertDialog mDialog; @@ -60,6 +64,7 @@ public class MediaProjectionPermissionActivity extends Activity public void onCreate(Bundle icicle) { super.onCreate(icicle); + mFeatureFlags = Dependency.get(FeatureFlags.class); mPackageName = getCallingPackage(); IBinder b = ServiceManager.getService(MEDIA_PROJECTION_SERVICE); mService = IMediaProjectionManager.Stub.asInterface(b); @@ -141,14 +146,22 @@ public class MediaProjectionPermissionActivity extends Activity dialogTitle = getString(R.string.media_projection_dialog_title, appName); } - mDialog = new AlertDialog.Builder(this, R.style.Theme_SystemUI_Dialog) + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this, + R.style.Theme_SystemUI_Dialog) .setTitle(dialogTitle) .setIcon(R.drawable.ic_media_projection_permission) .setMessage(dialogText) .setPositiveButton(R.string.media_projection_action_text, this) .setNeutralButton(android.R.string.cancel, this) - .setOnCancelListener(this) - .create(); + .setOnCancelListener(this); + + if (isPartialScreenSharingEnabled()) { + // This is a temporary entry point before we have a new permission dialog + // TODO(b/233183090): this activity should be redesigned to have a dropdown selector + dialogBuilder.setNegativeButton("App", this); + } + + mDialog = dialogBuilder.create(); SystemUIDialog.registerDismissListener(mDialog); SystemUIDialog.applyFlags(mDialog); @@ -177,6 +190,15 @@ public class MediaProjectionPermissionActivity extends Activity if (which == AlertDialog.BUTTON_POSITIVE) { setResult(RESULT_OK, getMediaProjectionIntent(mUid, mPackageName)); } + + if (isPartialScreenSharingEnabled() && which == AlertDialog.BUTTON_NEGATIVE) { + IMediaProjection projection = createProjection(mUid, mPackageName); + final Intent intent = new Intent(this, MediaProjectionAppSelectorActivity.class); + intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, + projection.asBinder()); + intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); + startActivity(intent); + } } catch (RemoteException e) { Log.e(TAG, "Error granting projection permission", e); setResult(RESULT_CANCELED); @@ -188,10 +210,14 @@ public class MediaProjectionPermissionActivity extends Activity } } + private IMediaProjection createProjection(int uid, String packageName) throws RemoteException { + return mService.createProjection(uid, packageName, + MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */); + } + private Intent getMediaProjectionIntent(int uid, String packageName) throws RemoteException { - IMediaProjection projection = mService.createProjection(uid, packageName, - MediaProjectionManager.TYPE_SCREEN_CAPTURE, false /* permanentGrant */); + IMediaProjection projection = createProjection(uid, packageName); Intent intent = new Intent(); intent.putExtra(MediaProjectionManager.EXTRA_MEDIA_PROJECTION, projection.asBinder()); return intent; @@ -201,4 +227,8 @@ public class MediaProjectionPermissionActivity extends Activity public void onCancel(DialogInterface dialog) { finish(); } + + private boolean isPartialScreenSharingEnabled() { + return mFeatureFlags.isEnabled(Flags.WM_ENABLE_PARTIAL_SCREEN_SHARING); + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt new file mode 100644 index 000000000000..e33a1b909d48 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.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.media.dagger + +import android.app.Activity +import com.android.systemui.media.MediaProjectionAppSelectorActivity +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +abstract class MediaProjectionModule { + + @Binds + @IntoMap + @ClassKey(MediaProjectionAppSelectorActivity::class) + abstract fun provideMediaProjectionAppSelectorActivity( + activity: MediaProjectionAppSelectorActivity): Activity + +} diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java index f2f275323d58..e9b6af44ddf3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java @@ -42,14 +42,11 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { private static final String TAG = "MediaOutputAdapter"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - private final MediaOutputDialog mMediaOutputDialog; private ViewGroup mConnectedItem; private boolean mIncludeDynamicGroup; - public MediaOutputAdapter(MediaOutputController controller, - MediaOutputDialog mediaOutputDialog) { + public MediaOutputAdapter(MediaOutputController controller) { super(controller); - mMediaOutputDialog = mediaOutputDialog; setHasStableIds(true); } @@ -63,7 +60,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { @Override public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) { final int size = mController.getMediaDevices().size(); - if (position == size && mController.isZeroMode()) { + if (position == size) { viewHolder.onBind(CUSTOMIZED_ITEM_PAIR_NEW, false /* topMargin */, true /* bottomMargin */); } else if (position < size) { @@ -78,7 +75,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { @Override public long getItemId(int position) { final int size = mController.getMediaDevices().size(); - if (position == size && mController.isZeroMode()) { + if (position == size) { return -1; } else if (position < size) { return ((List<MediaDevice>) (mController.getMediaDevices())) @@ -91,11 +88,8 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter { @Override public int getItemCount() { - if (mController.isZeroMode()) { - // Add extra one for "pair new" or dynamic group - return mController.getMediaDevices().size() + 1; - } - return mController.getMediaDevices().size(); + // Add extra one for "pair new" + return mController.getMediaDevices().size() + 1; } class MediaDeviceViewHolder extends MediaDeviceBaseViewHolder { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java index e5913061ef3f..0d5cab688d72 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java @@ -91,6 +91,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements private TextView mHeaderSubtitle; private ImageView mHeaderIcon; private ImageView mAppResourceIcon; + private ImageView mBroadcastIcon; private RecyclerView mDevicesRecyclerView; private LinearLayout mDeviceListLayout; private LinearLayout mCastAppLayout; @@ -239,6 +240,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements mAppButton = mDialogView.requireViewById(R.id.launch_app_button); mAppResourceIcon = mDialogView.requireViewById(R.id.app_source_icon); mCastAppLayout = mDialogView.requireViewById(R.id.cast_app_section); + mBroadcastIcon = mDialogView.requireViewById(R.id.broadcast_icon); mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener( mDeviceListLayoutListener); @@ -366,6 +368,9 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements mStopButton.setEnabled(true); mStopButton.setText(getStopButtonText()); mStopButton.setOnClickListener(v -> onStopButtonClick()); + + mBroadcastIcon.setVisibility(getBroadcastIconVisibility()); + mBroadcastIcon.setOnClickListener(v -> onBroadcastIconClick()); } private void updateButtonBackgroundColorFilter() { @@ -490,6 +495,14 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements dismiss(); } + public int getBroadcastIconVisibility() { + return View.GONE; + } + + public void onBroadcastIconClick() { + // Do nothing. + } + public boolean isBroadcastSupported() { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java index 8f065461c22d..310469dd5415 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java @@ -74,7 +74,7 @@ public class MediaOutputBroadcastDialog extends MediaOutputBaseDialog { MediaOutputBroadcastDialog(Context context, boolean aboveStatusbar, BroadcastSender broadcastSender, MediaOutputController mediaOutputController) { super(context, broadcastSender, mediaOutputController); - mAdapter = new MediaOutputGroupAdapter(mMediaOutputController); + mAdapter = new MediaOutputAdapter(mMediaOutputController); // TODO(b/226710953): Move the part to MediaOutputBaseDialog for every class // that extends MediaOutputBaseDialog if (!aboveStatusbar) { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index 247ffa7c2ef9..7e3275da8c31 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -16,7 +16,7 @@ package com.android.systemui.media.dialog; -import static android.provider.Settings.ACTION_BLUETOOTH_PAIRING_SETTINGS; +import static android.provider.Settings.ACTION_BLUETOOTH_SETTINGS; import android.annotation.CallbackExecutor; import android.app.AlertDialog; @@ -300,6 +300,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return; } try { + synchronized (mMediaDevicesLock) { + mMediaDevices.removeIf(MediaDevice::isMutingExpectedDevice); + } mAudioManager.cancelMuteAwaitConnection(mAudioManager.getMutingExpectedDevice()); } catch (Exception e) { Log.d(TAG, "Unable to cancel mute await connection"); @@ -711,22 +714,6 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, return false; } - boolean isZeroMode() { - synchronized (mMediaDevicesLock) { - if (mMediaDevices.size() == 1) { - final MediaDevice device = mMediaDevices.iterator().next(); - // Add "pair new" only when local output device exists - final int type = device.getDeviceType(); - if (type == MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE - || type == MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE - || type == MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE) { - return true; - } - } - return false; - } - } - void launchBluetoothPairing(View view) { ActivityLaunchAnimator.Controller controller = mDialogLaunchAnimator.createActivityLaunchController(view); @@ -736,7 +723,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, } Intent launchIntent = - new Intent(ACTION_BLUETOOTH_PAIRING_SETTINGS) + new Intent(ACTION_BLUETOOTH_SETTINGS) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); final Intent deepLinkIntent = new Intent(Settings.ACTION_SETTINGS_EMBED_DEEP_LINK_ACTIVITY); diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java index a6cf408e0099..fc4773d3c6dd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java @@ -42,7 +42,7 @@ public class MediaOutputDialog extends MediaOutputBaseDialog { MediaOutputController mediaOutputController, UiEventLogger uiEventLogger) { super(context, broadcastSender, mediaOutputController); mUiEventLogger = uiEventLogger; - mAdapter = new MediaOutputAdapter(mMediaOutputController, this); + mAdapter = new MediaOutputAdapter(mMediaOutputController); if (!aboveStatusbar) { getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } @@ -134,6 +134,17 @@ public class MediaOutputDialog extends MediaOutputBaseDialog { } } + @Override + public int getBroadcastIconVisibility() { + return (isBroadcastSupported() && mMediaOutputController.isBluetoothLeBroadcastEnabled()) + ? View.VISIBLE : View.GONE; + } + + @Override + public void onBroadcastIconClick() { + startLeBroadcastDialog(); + } + @VisibleForTesting public enum MediaOutputEvent implements UiEventLogger.UiEventEnum { @UiEvent(doc = "The MediaOutput dialog became visible on the screen.") diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java deleted file mode 100644 index ba2f006fc58e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupAdapter.java +++ /dev/null @@ -1,172 +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.systemui.media.dialog; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.util.Log; -import android.util.TypedValue; -import android.view.View; -import android.view.ViewGroup; - -import androidx.annotation.NonNull; - -import com.android.settingslib.media.MediaDevice; -import com.android.systemui.R; - -import java.util.List; - -/** - * Adapter for media output dynamic group dialog. - */ -//TODO: clear this class after new UI updated -public class MediaOutputGroupAdapter extends MediaOutputBaseAdapter { - - private static final String TAG = "MediaOutputGroupAdapter"; - private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); - - private final List<MediaDevice> mGroupMediaDevices; - - public MediaOutputGroupAdapter(MediaOutputController controller) { - super(controller); - mGroupMediaDevices = controller.getGroupMediaDevices(); - } - - @Override - public MediaDeviceBaseViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, - int viewType) { - super.onCreateViewHolder(viewGroup, viewType); - - return new GroupViewHolder(mHolderView); - } - - @Override - public void onBindViewHolder(@NonNull MediaDeviceBaseViewHolder viewHolder, int position) { - // Add "Group" - if (position == 0) { - viewHolder.onBind(CUSTOMIZED_ITEM_GROUP, true /* topMargin */, - false /* bottomMargin */); - return; - } - // Add available devices - final int newPosition = position - 1; - final int size = mGroupMediaDevices.size(); - if (newPosition < size) { - viewHolder.onBind(mGroupMediaDevices.get(newPosition), false /* topMargin */, - newPosition == (size - 1) /* bottomMargin */, position); - return; - } - if (DEBUG) { - Log.d(TAG, "Incorrect position: " + position); - } - } - - @Override - public int getItemCount() { - // Require extra item for group volume operation - return mGroupMediaDevices.size() + 1; - } - - @Override - CharSequence getItemTitle(MediaDevice device) { - return super.getItemTitle(device); - } - - class GroupViewHolder extends MediaDeviceBaseViewHolder { - - GroupViewHolder(View view) { - super(view); - } - - @Override - void onBind(MediaDevice device, boolean topMargin, boolean bottomMargin, int position) { - super.onBind(device, topMargin, bottomMargin, position); - mCheckBox.setVisibility(View.VISIBLE); - mCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> { - onCheckBoxClicked(isChecked, device); - }); - boolean isCurrentSeekbarInvisible = mSeekBar.getVisibility() == View.GONE; - setTwoLineLayout(device, false /* bFocused */, true /* showSeekBar */, - false /* showProgressBar */, false /* showSubtitle*/); - initSeekbar(device, isCurrentSeekbarInvisible); - final List<MediaDevice> selectedDevices = mController.getSelectedMediaDevice(); - if (isDeviceIncluded(mController.getSelectableMediaDevice(), device)) { - mCheckBox.setButtonDrawable(R.drawable.ic_check_box); - mCheckBox.setChecked(false); - mCheckBox.setEnabled(true); - } else if (isDeviceIncluded(selectedDevices, device)) { - if (selectedDevices.size() == 1 || !isDeviceIncluded( - mController.getDeselectableMediaDevice(), device)) { - mCheckBox.setButtonDrawable(getDisabledCheckboxDrawable()); - mCheckBox.setChecked(true); - mCheckBox.setEnabled(false); - } else { - mCheckBox.setButtonDrawable(R.drawable.ic_check_box); - mCheckBox.setChecked(true); - mCheckBox.setEnabled(true); - } - } - } - - @Override - void onBind(int customizedItem, boolean topMargin, boolean bottomMargin) { - if (customizedItem == CUSTOMIZED_ITEM_GROUP) { - setTwoLineLayout(mContext.getText(R.string.media_output_dialog_group), - true /* bFocused */, true /* showSeekBar */, false /* showProgressBar */, - false /* showSubtitle*/); - mTitleIcon.setImageDrawable(getSpeakerDrawable()); - mCheckBox.setVisibility(View.GONE); - initSessionSeekbar(); - } - } - - private void onCheckBoxClicked(boolean isChecked, MediaDevice device) { - if (isChecked && isDeviceIncluded(mController.getSelectableMediaDevice(), device)) { - mController.addDeviceToPlayMedia(device); - } else if (!isChecked && isDeviceIncluded(mController.getDeselectableMediaDevice(), - device)) { - mController.removeDeviceFromPlayMedia(device); - } - } - - private Drawable getDisabledCheckboxDrawable() { - final Drawable drawable = mContext.getDrawable(R.drawable.ic_check_box_blue_24dp) - .mutate(); - final Bitmap checkbox = Bitmap.createBitmap(drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(checkbox); - TypedValue value = new TypedValue(); - mContext.getTheme().resolveAttribute(android.R.attr.disabledAlpha, value, true); - drawable.setAlpha((int) (value.getFloat() * 255)); - drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); - drawable.draw(canvas); - - return drawable; - } - - private boolean isDeviceIncluded(List<MediaDevice> deviceList, MediaDevice targetDevice) { - for (MediaDevice device : deviceList) { - if (TextUtils.equals(device.getId(), targetDevice.getId())) { - return true; - } - } - return false; - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java deleted file mode 100644 index bb3f969c86df..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputGroupDialog.java +++ /dev/null @@ -1,97 +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.systemui.media.dialog; - -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.view.View; -import android.view.WindowManager; - -import androidx.core.graphics.drawable.IconCompat; - -import com.android.systemui.R; -import com.android.systemui.broadcast.BroadcastSender; - -/** - * Dialog for media output group. - */ -// TODO(b/203073091): Remove this class once group logic been implemented. -public class MediaOutputGroupDialog extends MediaOutputBaseDialog { - - MediaOutputGroupDialog(Context context, boolean aboveStatusbar, BroadcastSender broadcastSender, - MediaOutputController mediaOutputController) { - super(context, broadcastSender, mediaOutputController); - mMediaOutputController.resetGroupMediaDevices(); - mAdapter = new MediaOutputGroupAdapter(mMediaOutputController); - if (!aboveStatusbar) { - getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); - } - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - } - - @Override - int getHeaderIconRes() { - return R.drawable.ic_arrow_back; - } - - @Override - IconCompat getHeaderIcon() { - return null; - } - - @Override - int getHeaderIconSize() { - return mContext.getResources().getDimensionPixelSize( - R.dimen.media_output_dialog_header_back_icon_size); - } - - @Override - CharSequence getHeaderText() { - return mContext.getString(R.string.media_output_dialog_add_output); - } - - @Override - CharSequence getHeaderSubtitle() { - final int size = mMediaOutputController.getSelectedMediaDevice().size(); - if (size == 1) { - return mContext.getText(R.string.media_output_dialog_single_device); - } - return mContext.getString(R.string.media_output_dialog_multiple_devices, size); - } - - @Override - Drawable getAppSourceIcon() { - return null; - } - - @Override - int getStopButtonVisibility() { - return View.VISIBLE; - } - - @Override - void onHeaderIconClick() { - // Given that we launched the media output group dialog from the media output dialog, - // dismissing this dialog will show the media output dialog again. - dismiss(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 5f52485a5481..281ef9495d8e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -626,6 +626,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements } }, mainExecutor, bgExecutor); + mView.setBackgroundExecutor(bgExecutor); mView.setEdgeBackGestureHandler(mEdgeBackGestureHandler); mNavBarMode = mNavigationModeController.addListener(mModeChangedListener); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index ad3cfa359a52..9e1ba5f723a3 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -72,6 +72,7 @@ import com.android.systemui.navigationbar.buttons.NearestTouchFrame; import com.android.systemui.navigationbar.buttons.RotationContextButton; import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.recents.Recents; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shared.rotation.FloatingRotationButton; import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback; import com.android.systemui.shared.rotation.RotationButtonController; @@ -81,13 +82,13 @@ import com.android.systemui.shared.system.WindowManagerWrapper; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LightBarTransitionsController; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.pip.Pip; import java.io.PrintWriter; import java.util.Map; import java.util.Optional; +import java.util.concurrent.Executor; import java.util.function.Consumer; /** */ @@ -97,6 +98,8 @@ public class NavigationBarView extends FrameLayout { final static boolean ALTERNATE_CAR_MODE_UI = false; + private Executor mBgExecutor; + // The current view is one of mHorizontal or mVertical depending on the current configuration View mCurrentView = null; private View mVertical; @@ -349,6 +352,10 @@ public class NavigationBarView extends FrameLayout { notifyVerticalChangedListener(mIsVertical); } + public void setBackgroundExecutor(Executor bgExecutor) { + mBgExecutor = bgExecutor; + } + public void setTouchHandler(Gefingerpoken touchHandler) { mTouchHandler = touchHandler; } @@ -768,8 +775,8 @@ public class NavigationBarView extends FrameLayout { updateSlippery(); reloadNavIcons(); updateNavButtonIcons(); - WindowManagerWrapper.getInstance().setNavBarVirtualKeyHapticFeedbackEnabled( - !mShowSwipeUpUi); + mBgExecutor.execute(() -> WindowManagerWrapper.getInstance() + .setNavBarVirtualKeyHapticFeedbackEnabled(!mShowSwipeUpUi)); getHomeButton().setAccessibilityDelegate( mShowSwipeUpUi ? mQuickStepAccessibilityDelegate : null); } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt index 8179d1763ea1..28ab83c83a42 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt @@ -30,6 +30,7 @@ import android.view.Gravity import android.view.MotionEvent import android.view.VelocityTracker import android.view.View +import android.view.ViewConfiguration import android.view.WindowManager import android.view.animation.DecelerateInterpolator import android.view.animation.PathInterpolator @@ -98,6 +99,7 @@ class BackPanelController private constructor( context: Context, private var backAnimation: BackAnimation?, private val windowManager: WindowManager, + private val viewConfiguration: ViewConfiguration, @Main private val mainHandler: Handler, private val vibratorHelper: VibratorHelper, private val configurationController: ConfigurationController, @@ -112,6 +114,7 @@ class BackPanelController private constructor( */ class Factory @Inject constructor( private val windowManager: WindowManager, + private val viewConfiguration: ViewConfiguration, @Main private val mainHandler: Handler, private val vibratorHelper: VibratorHelper, private val configurationController: ConfigurationController, @@ -123,6 +126,7 @@ class BackPanelController private constructor( context, backAnimation, windowManager, + viewConfiguration, mainHandler, vibratorHelper, configurationController, @@ -164,6 +168,10 @@ class BackPanelController private constructor( private var gestureStartTime = 0L + // Whether the current gesture has moved a sufficiently large amount, + // so that we can unambiguously start showing the ENTRY animation + private var hasPassedDragSlop = false + private val failsafeRunnable = Runnable { onFailsafe() } private enum class GestureState { @@ -304,29 +312,53 @@ class BackPanelController private constructor( startX = event.x startY = event.y gestureStartTime = SystemClock.uptimeMillis() - - // Reset the arrow to the side - updateArrowState(GestureState.ENTRY) - - windowManager.updateViewLayout(mView, layoutParams) - mView.startTrackingShowBackArrowLatency() } - MotionEvent.ACTION_MOVE -> handleMoveEvent(event) + MotionEvent.ACTION_MOVE -> { + // only go to the ENTRY state after some minimum motion has occurred + if (dragSlopExceeded(event.x, startX)) { + handleMoveEvent(event) + } + } MotionEvent.ACTION_UP -> { if (currentState == GestureState.ACTIVE) { updateArrowState(if (isFlung()) GestureState.FLUNG else GestureState.COMMITTED) - } else { + } else if (currentState != GestureState.GONE) { // if invisible, skip animation updateArrowState(GestureState.CANCELLED) } velocityTracker = null } MotionEvent.ACTION_CANCEL -> { - updateArrowState(GestureState.CANCELLED) + // Receiving a CANCEL implies that something else intercepted + // the gesture, i.e., the user did not cancel their gesture. + // Therefore, disappear immediately, with minimum fanfare. + updateArrowState(GestureState.GONE) velocityTracker = null } } } + /** + * Returns false until the current gesture exceeds the touch slop threshold, + * and returns true thereafter (we reset on the subsequent back gesture). + * The moment it switches from false -> true is important, + * because that's when we switch state, from GONE -> ENTRY. + * @return whether the current gesture has moved past a minimum threshold. + */ + private fun dragSlopExceeded(curX: Float, startX: Float): Boolean { + if (hasPassedDragSlop) return true + + if (abs(curX - startX) > viewConfiguration.scaledTouchSlop) { + // Reset the arrow to the side + updateArrowState(GestureState.ENTRY) + + windowManager.updateViewLayout(mView, layoutParams) + mView.startTrackingShowBackArrowLatency() + + hasPassedDragSlop = true + } + return hasPassedDragSlop + } + private fun updateArrowStateOnMove(yTranslation: Float, xTranslation: Float) { if (!currentState.isInteractive()) return @@ -530,6 +562,7 @@ class BackPanelController private constructor( } private fun resetOnDown() { + hasPassedDragSlop = false hasHapticPlayed = false totalTouchDelta = 0f vibrationTime = 0 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 3039d9d56c80..067f4cbb975e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -53,13 +53,11 @@ import android.view.MotionEvent; import android.view.Surface; import android.view.ViewConfiguration; import android.view.WindowManager; -import android.view.WindowMetrics; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.internal.policy.GestureNavigationSettingsObserver; import com.android.internal.util.LatencyTracker; 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; @@ -96,6 +94,7 @@ import java.util.concurrent.Executor; import java.util.function.Consumer; import javax.inject.Inject; +import javax.inject.Provider; /** * Utility class to handle edge swipes for back gesture @@ -133,7 +132,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker @Override public void onPrioritizedRotation(@Surface.Rotation int rotation) { mStartingQuickstepRotation = rotation; - updateDisabledForQuickstep(mContext.getResources().getConfiguration()); + updateDisabledForQuickstep(mLastReportedConfig); } }; @@ -183,6 +182,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private final IWindowManager mWindowManagerService; private final Optional<Pip> mPipOptional; private final FalsingManager mFalsingManager; + private final Configuration mLastReportedConfig = new Configuration(); // Activities which should not trigger Back gesture. private final List<ComponentName> mGestureBlockingActivities = new ArrayList<>(); @@ -196,6 +196,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private final Region mExcludeRegion = new Region(); private final Region mUnrestrictedExcludeRegion = new Region(); private final LatencyTracker mLatencyTracker; + private final Provider<BackGestureTfClassifierProvider> + mBackGestureTfClassifierProviderProvider; private final FeatureFlags mFeatureFlags; // The left side edge width where touch down is allowed @@ -316,6 +318,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker Optional<Pip> pipOptional, FalsingManager falsingManager, LatencyTracker latencyTracker, + Provider<BackGestureTfClassifierProvider> backGestureTfClassifierProviderProvider, FeatureFlags featureFlags) { super(broadcastDispatcher); mContext = context; @@ -333,7 +336,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mPipOptional = pipOptional; mFalsingManager = falsingManager; mLatencyTracker = latencyTracker; + mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider; mFeatureFlags = featureFlags; + mLastReportedConfig.setTo(mContext.getResources().getConfiguration()); ComponentName recentsComponentName = ComponentName.unflattenFromString( context.getString(com.android.internal.R.string.config_recentsComponentName)); if (recentsComponentName != null) { @@ -612,10 +617,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker } if (newState) { - String mlModelName = DeviceConfig.getString(DeviceConfig.NAMESPACE_SYSTEMUI, - SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_NAME, "backgesture"); - mBackGestureTfClassifierProvider = SystemUIFactory.getInstance() - .createBackGestureTfClassifierProvider(mContext.getAssets(), mlModelName); + mBackGestureTfClassifierProvider = mBackGestureTfClassifierProviderProvider.get(); mMLModelThreshold = DeviceConfig.getFloat(DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.BACK_GESTURE_ML_MODEL_THRESHOLD, 0.9f); if (mBackGestureTfClassifierProvider.isActive()) { @@ -885,12 +887,12 @@ public class EdgeBackGestureHandler extends CurrentUserTracker if (DEBUG_MISSING_GESTURE) { Log.d(DEBUG_MISSING_GESTURE_TAG, "Config changed: config=" + newConfig); } + mLastReportedConfig.updateFrom(newConfig); updateDisplaySize(); } private void updateDisplaySize() { - WindowMetrics metrics = mWindowManager.getMaximumWindowMetrics(); - Rect bounds = metrics.getBounds(); + Rect bounds = mLastReportedConfig.windowConfiguration.getMaxBounds(); mDisplaySize.set(bounds.width(), bounds.height()); if (DEBUG_MISSING_GESTURE) { Log.d(DEBUG_MISSING_GESTURE_TAG, "Update display size: mDisplaySize=" + mDisplaySize); @@ -1007,6 +1009,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker private final Optional<Pip> mPipOptional; private final FalsingManager mFalsingManager; private final LatencyTracker mLatencyTracker; + private final Provider<BackGestureTfClassifierProvider> + mBackGestureTfClassifierProviderProvider; private final FeatureFlags mFeatureFlags; @Inject @@ -1024,6 +1028,8 @@ public class EdgeBackGestureHandler extends CurrentUserTracker Optional<Pip> pipOptional, FalsingManager falsingManager, LatencyTracker latencyTracker, + Provider<BackGestureTfClassifierProvider> + backGestureTfClassifierProviderProvider, FeatureFlags featureFlags) { mOverviewProxyService = overviewProxyService; mSysUiState = sysUiState; @@ -1039,6 +1045,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mPipOptional = pipOptional; mFalsingManager = falsingManager; mLatencyTracker = latencyTracker; + mBackGestureTfClassifierProviderProvider = backGestureTfClassifierProviderProvider; mFeatureFlags = featureFlags; } @@ -1060,6 +1067,7 @@ public class EdgeBackGestureHandler extends CurrentUserTracker mPipOptional, mFalsingManager, mLatencyTracker, + mBackGestureTfClassifierProviderProvider, mFeatureFlags); } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/GestureModule.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/GestureModule.java new file mode 100644 index 000000000000..f98496d43730 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/GestureModule.java @@ -0,0 +1,32 @@ +/* + * 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 dagger.Module; +import dagger.Provides; + +/** + * + */ +@Module +public interface GestureModule { + /** */ + @Provides + static BackGestureTfClassifierProvider providsBackGestureTfClassifierProvider() { + return new BackGestureTfClassifierProvider(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleModule.kt b/packages/SystemUI/src/com/android/systemui/people/PeopleModule.kt new file mode 100644 index 000000000000..dd35445f9b7b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleModule.kt @@ -0,0 +1,32 @@ +/* + * 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.people + +import com.android.systemui.people.data.repository.PeopleTileRepository +import com.android.systemui.people.data.repository.PeopleTileRepositoryImpl +import com.android.systemui.people.data.repository.PeopleWidgetRepository +import com.android.systemui.people.data.repository.PeopleWidgetRepositoryImpl +import dagger.Binds +import dagger.Module + +/** Dagger module to provide/bind people space dependencies. */ +@Module +interface PeopleModule { + @Binds fun bindTileRepository(impl: PeopleTileRepositoryImpl): PeopleTileRepository + + @Binds fun bindWidgetRepository(impl: PeopleWidgetRepositoryImpl): PeopleWidgetRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java index b55d86e8fb1c..0ba077eb0912 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleProvider.java @@ -29,7 +29,8 @@ import android.os.UserHandle; import android.util.Log; import android.widget.RemoteViews; -import com.android.systemui.SystemUIAppComponentFactory; +import com.android.systemui.SystemUIAppComponentFactoryBase.ContextAvailableCallback; +import com.android.systemui.SystemUIAppComponentFactoryBase.ContextInitializer; import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.shared.system.PeopleProviderUtils; @@ -37,11 +38,11 @@ import javax.inject.Inject; /** API that returns a People Tile preview. */ public class PeopleProvider extends ContentProvider implements - SystemUIAppComponentFactory.ContextInitializer { + ContextInitializer { private static final String TAG = "PeopleProvider"; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; private static final String EMPTY_STRING = ""; - private SystemUIAppComponentFactory.ContextAvailableCallback mCallback; + private ContextAvailableCallback mCallback; @Inject PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; @@ -144,7 +145,7 @@ public class PeopleProvider extends ContentProvider implements @Override public void setContextAvailableCallback( - SystemUIAppComponentFactory.ContextAvailableCallback callback) { + ContextAvailableCallback callback) { mCallback = callback; } } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index 93a3f81fdd6b..e845aa85a121 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -19,144 +19,52 @@ package com.android.systemui.people; import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID; import static android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID; -import static com.android.systemui.people.PeopleTileViewHelper.getPersonIconBitmap; -import static com.android.systemui.people.PeopleTileViewHelper.getSizeInDp; - -import android.app.Activity; -import android.app.people.PeopleSpaceTile; -import android.content.Context; import android.content.Intent; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.Outline; -import android.graphics.drawable.GradientDrawable; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.ViewGroup; -import android.view.ViewOutlineProvider; -import android.widget.LinearLayout; -import com.android.systemui.R; -import com.android.systemui.people.widget.PeopleSpaceWidgetManager; -import com.android.systemui.people.widget.PeopleTileKey; +import androidx.activity.ComponentActivity; +import androidx.lifecycle.ViewModelProvider; -import java.util.ArrayList; -import java.util.List; +import com.android.systemui.people.ui.view.PeopleViewBinder; +import com.android.systemui.people.ui.viewmodel.PeopleViewModel; import javax.inject.Inject; /** People Tile Widget configuration activity that shows the user their conversation tiles. */ -public class PeopleSpaceActivity extends Activity { +public class PeopleSpaceActivity extends ComponentActivity { private static final String TAG = "PeopleSpaceActivity"; private static final boolean DEBUG = PeopleSpaceUtils.DEBUG; - private PeopleSpaceWidgetManager mPeopleSpaceWidgetManager; - private Context mContext; - private int mAppWidgetId; + private final PeopleViewModel.Factory mViewModelFactory; + private PeopleViewModel mViewModel; @Inject - public PeopleSpaceActivity(PeopleSpaceWidgetManager peopleSpaceWidgetManager) { + public PeopleSpaceActivity(PeopleViewModel.Factory viewModelFactory) { super(); - mPeopleSpaceWidgetManager = peopleSpaceWidgetManager; - + mViewModelFactory = viewModelFactory; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mContext = getApplicationContext(); - mAppWidgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, - INVALID_APPWIDGET_ID); setResult(RESULT_CANCELED); - } - - /** Builds the conversation selection activity. */ - private void buildActivity() { - List<PeopleSpaceTile> priorityTiles = new ArrayList<>(); - List<PeopleSpaceTile> recentTiles = new ArrayList<>(); - try { - priorityTiles = mPeopleSpaceWidgetManager.getPriorityTiles(); - recentTiles = mPeopleSpaceWidgetManager.getRecentTiles(); - } catch (Exception e) { - Log.e(TAG, "Couldn't retrieve conversations", e); - } - - // If no conversations, render activity without conversations - if (recentTiles.isEmpty() && priorityTiles.isEmpty()) { - setContentView(R.layout.people_space_activity_no_conversations); - - // The Tile preview has colorBackground as its background. Change it so it's different - // than the activity's background. - LinearLayout item = findViewById(android.R.id.background); - GradientDrawable shape = (GradientDrawable) item.getBackground(); - final TypedArray ta = mContext.getTheme().obtainStyledAttributes( - new int[]{com.android.internal.R.attr.colorSurface}); - shape.setColor(ta.getColor(0, Color.WHITE)); - return; - } - - setContentView(R.layout.people_space_activity); - setTileViews(R.id.priority, R.id.priority_tiles, priorityTiles); - setTileViews(R.id.recent, R.id.recent_tiles, recentTiles); - } - - private ViewOutlineProvider mViewOutlineProvider = new ViewOutlineProvider() { - @Override - public void getOutline(View view, Outline outline) { - outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), - mContext.getResources().getDimension(R.dimen.people_space_widget_radius)); - } - }; - - /** Sets a {@link PeopleSpaceTileView}s for each conversation. */ - private void setTileViews(int viewId, int tilesId, List<PeopleSpaceTile> tiles) { - if (tiles.isEmpty()) { - LinearLayout view = findViewById(viewId); - view.setVisibility(View.GONE); - return; - } - - ViewGroup layout = findViewById(tilesId); - layout.setClipToOutline(true); - layout.setOutlineProvider(mViewOutlineProvider); - for (int i = 0; i < tiles.size(); ++i) { - PeopleSpaceTile tile = tiles.get(i); - PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, - layout, tile.getId(), i == (tiles.size() - 1)); - setTileView(tileView, tile); - } - } - - /** Sets {@code tileView} with the data in {@code conversation}. */ - private void setTileView(PeopleSpaceTileView tileView, PeopleSpaceTile tile) { - try { - if (tile.getUserName() != null) { - tileView.setName(tile.getUserName().toString()); - } - tileView.setPersonIcon(getPersonIconBitmap(mContext, tile, - getSizeInDp(mContext, R.dimen.avatar_size_for_medium, - mContext.getResources().getDisplayMetrics().density))); - - PeopleTileKey key = new PeopleTileKey(tile); - tileView.setOnClickListener(v -> storeWidgetConfiguration(tile, key)); - } catch (Exception e) { - Log.e(TAG, "Couldn't retrieve shortcut information", e); - } - } - - /** Stores the user selected configuration for {@code mAppWidgetId}. */ - private void storeWidgetConfiguration(PeopleSpaceTile tile, PeopleTileKey key) { - if (PeopleSpaceUtils.DEBUG) { - if (DEBUG) { - Log.d(TAG, "Put " + tile.getUserName() + "'s shortcut ID: " - + tile.getId() + " for widget ID: " - + mAppWidgetId); - } - } - mPeopleSpaceWidgetManager.addNewWidget(mAppWidgetId, key); - finishActivity(); + mViewModel = new ViewModelProvider(this, mViewModelFactory).get(PeopleViewModel.class); + + // Update the widget ID coming from the intent. + int widgetId = getIntent().getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID); + mViewModel.onWidgetIdChanged(widgetId); + + ViewGroup view = PeopleViewBinder.create(this); + PeopleViewBinder.bind(view, mViewModel, /* lifecycleOwner= */ this, + () -> { + finishActivity(); + return null; + }); + setContentView(view); } /** Finish activity with a successful widget configuration result. */ @@ -169,19 +77,13 @@ public class PeopleSpaceActivity extends Activity { /** Finish activity without choosing a widget. */ public void dismissActivity(View v) { if (DEBUG) Log.d(TAG, "Activity dismissed with no widgets added!"); + setResult(RESULT_CANCELED); finish(); } private void setActivityResult(int result) { Intent resultValue = new Intent(); - resultValue.putExtra(EXTRA_APPWIDGET_ID, mAppWidgetId); + resultValue.putExtra(EXTRA_APPWIDGET_ID, mViewModel.getAppWidgetId().getValue()); setResult(result, resultValue); } - - @Override - protected void onResume() { - super.onResume(); - // Refresh tile views to sync new conversations. - buildActivity(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java index 4ee951f3cdb1..58e700f81388 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleStoryIconFactory.java @@ -28,6 +28,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.IconDrawableFactory; import android.util.Log; +import android.view.ContextThemeWrapper; import androidx.core.graphics.drawable.RoundedBitmapDrawable; @@ -52,16 +53,15 @@ class PeopleStoryIconFactory implements AutoCloseable { PeopleStoryIconFactory(Context context, PackageManager pm, IconDrawableFactory iconDrawableFactory, int iconSizeDp) { - context.setTheme(android.R.style.Theme_DeviceDefault_DayNight); - mIconBitmapSize = (int) (iconSizeDp * context.getResources().getDisplayMetrics().density); - mDensity = context.getResources().getDisplayMetrics().density; + mContext = new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault_DayNight); + mIconBitmapSize = (int) (iconSizeDp * mContext.getResources().getDisplayMetrics().density); + mDensity = mContext.getResources().getDisplayMetrics().density; mIconSize = mDensity * iconSizeDp; mPackageManager = pm; mIconDrawableFactory = iconDrawableFactory; - mImportantConversationColor = context.getColor(R.color.important_conversation); - mAccentColor = Utils.getColorAttr(context, + mImportantConversationColor = mContext.getColor(R.color.important_conversation); + mAccentColor = Utils.getColorAttr(mContext, com.android.internal.R.attr.colorAccentPrimaryVariant).getDefaultColor(); - mContext = context; } diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java index 00aa1381ace1..be82b1faac8e 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleTileViewHelper.java @@ -75,6 +75,7 @@ import androidx.core.math.MathUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; +import com.android.systemui.people.data.model.PeopleTileModel; import com.android.systemui.people.widget.LaunchConversationActivity; import com.android.systemui.people.widget.PeopleSpaceWidgetProvider; import com.android.systemui.people.widget.PeopleTileKey; @@ -299,7 +300,8 @@ public class PeopleTileViewHelper { return createLastInteractionRemoteViews(); } - private static boolean isDndBlockingTileData(@Nullable PeopleSpaceTile tile) { + /** Whether the conversation associated with {@code tile} can bypass DND. */ + public static boolean isDndBlockingTileData(@Nullable PeopleSpaceTile tile) { if (tile == null) return false; int notificationPolicyState = tile.getNotificationPolicyState(); @@ -536,7 +538,8 @@ public class PeopleTileViewHelper { return views; } - private static boolean getHasNewStory(PeopleSpaceTile tile) { + /** Whether {@code tile} has a new story. */ + public static boolean getHasNewStory(PeopleSpaceTile tile) { return tile.getStatuses() != null && tile.getStatuses().stream().anyMatch( c -> c.getActivity() == ACTIVITY_NEW_STORY); } @@ -1250,16 +1253,24 @@ public class PeopleTileViewHelper { } /** Returns a bitmap with the user icon and package icon. */ - public static Bitmap getPersonIconBitmap(Context context, PeopleSpaceTile tile, + public static Bitmap getPersonIconBitmap(Context context, PeopleTileModel tile, int maxAvatarSize) { - boolean hasNewStory = getHasNewStory(tile); - return getPersonIconBitmap(context, tile, maxAvatarSize, hasNewStory); + return getPersonIconBitmap(context, maxAvatarSize, tile.getHasNewStory(), + tile.getUserIcon(), tile.getKey().getPackageName(), tile.getKey().getUserId(), + tile.isImportant(), tile.isDndBlocking()); } /** Returns a bitmap with the user icon and package icon. */ private static Bitmap getPersonIconBitmap( Context context, PeopleSpaceTile tile, int maxAvatarSize, boolean hasNewStory) { - Icon icon = tile.getUserIcon(); + return getPersonIconBitmap(context, maxAvatarSize, hasNewStory, tile.getUserIcon(), + tile.getPackageName(), getUserId(tile), + tile.isImportantConversation(), isDndBlockingTileData(tile)); + } + + private static Bitmap getPersonIconBitmap( + Context context, int maxAvatarSize, boolean hasNewStory, Icon icon, String packageName, + int userId, boolean importantConversation, boolean dndBlockingTileData) { if (icon == null) { Drawable placeholder = context.getDrawable(R.drawable.ic_avatar_with_badge).mutate(); placeholder.setColorFilter(getDisabledColorFilter()); @@ -1272,10 +1283,10 @@ public class PeopleTileViewHelper { RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create( context.getResources(), icon.getBitmap()); Drawable personDrawable = storyIcon.getPeopleTileDrawable(roundedDrawable, - tile.getPackageName(), getUserId(tile), tile.isImportantConversation(), + packageName, userId, importantConversation, hasNewStory); - if (isDndBlockingTileData(tile)) { + if (dndBlockingTileData) { personDrawable.setColorFilter(getDisabledColorFilter()); } diff --git a/packages/SystemUI/src/com/android/systemui/people/data/model/PeopleTileModel.kt b/packages/SystemUI/src/com/android/systemui/people/data/model/PeopleTileModel.kt new file mode 100644 index 000000000000..5d8539fabc6b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/data/model/PeopleTileModel.kt @@ -0,0 +1,30 @@ +/* + * 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.people.data.model + +import android.graphics.drawable.Icon +import com.android.systemui.people.widget.PeopleTileKey + +/** Models a tile/conversation. */ +data class PeopleTileModel( + val key: PeopleTileKey, + val username: String, + val userIcon: Icon, + val hasNewStory: Boolean, + val isImportant: Boolean, + val isDndBlocking: Boolean, +) diff --git a/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleTileRepository.kt b/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleTileRepository.kt new file mode 100644 index 000000000000..01b43d52130b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleTileRepository.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.people.data.repository + +import android.app.people.PeopleSpaceTile +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.people.PeopleTileViewHelper +import com.android.systemui.people.data.model.PeopleTileModel +import com.android.systemui.people.widget.PeopleSpaceWidgetManager +import com.android.systemui.people.widget.PeopleTileKey +import javax.inject.Inject + +/** A Repository to fetch the current tiles/conversations. */ +// TODO(b/238993727): Make the tiles API reactive. +interface PeopleTileRepository { + /* The current priority tiles. */ + fun priorityTiles(): List<PeopleTileModel> + + /* The current recent tiles. */ + fun recentTiles(): List<PeopleTileModel> +} + +@SysUISingleton +class PeopleTileRepositoryImpl +@Inject +constructor( + private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager, +) : PeopleTileRepository { + override fun priorityTiles(): List<PeopleTileModel> { + return peopleSpaceWidgetManager.priorityTiles.map { it.toModel() } + } + + override fun recentTiles(): List<PeopleTileModel> { + return peopleSpaceWidgetManager.recentTiles.map { it.toModel() } + } + + private fun PeopleSpaceTile.toModel(): PeopleTileModel { + return PeopleTileModel( + PeopleTileKey(this), + userName.toString(), + userIcon, + PeopleTileViewHelper.getHasNewStory(this), + isImportantConversation, + PeopleTileViewHelper.isDndBlockingTileData(this), + ) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleWidgetRepository.kt new file mode 100644 index 000000000000..f2b6cb1fc3f5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/data/repository/PeopleWidgetRepository.kt @@ -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.people.data.repository + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.people.widget.PeopleSpaceWidgetManager +import com.android.systemui.people.widget.PeopleTileKey +import javax.inject.Inject + +interface PeopleWidgetRepository { + /** + * Bind the widget with ID [widgetId] to the tile keyed by [tileKey]. + * + * If there is already a widget with [widgetId], this existing widget will be reconfigured and + * associated to this tile. If there is no widget with [widgetId], a new one will be created. + */ + fun setWidgetTile(widgetId: Int, tileKey: PeopleTileKey) +} + +@SysUISingleton +class PeopleWidgetRepositoryImpl +@Inject +constructor( + private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager, +) : PeopleWidgetRepository { + override fun setWidgetTile(widgetId: Int, tileKey: PeopleTileKey) { + peopleSpaceWidgetManager.addNewWidget(widgetId, tileKey) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt new file mode 100644 index 000000000000..bc982cccaacd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt @@ -0,0 +1,243 @@ +/* + * 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.people.ui.view + +import android.content.Context +import android.graphics.Color +import android.graphics.Outline +import android.graphics.drawable.GradientDrawable +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.ViewOutlineProvider +import android.widget.LinearLayout +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.Lifecycle.State.CREATED +import androidx.lifecycle.LifecycleOwner +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.R +import com.android.systemui.people.PeopleSpaceTileView +import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel +import com.android.systemui.people.ui.viewmodel.PeopleViewModel +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.launch + +/** A ViewBinder for [PeopleViewModel]. */ +object PeopleViewBinder { + private const val TAG = "PeopleSpaceViewBinder" + + /** + * The [ViewOutlineProvider] used to clip the corner radius of the recent and priority lists. + */ + private val ViewOutlineProvider = + object : ViewOutlineProvider() { + override fun getOutline(view: View, outline: Outline) { + outline.setRoundRect( + 0, + 0, + view.width, + view.height, + view.context.resources.getDimension(R.dimen.people_space_widget_radius), + ) + } + } + + /** Create a [View] that can later be [bound][bind] to a [PeopleViewModel]. */ + @JvmStatic + fun create(context: Context): ViewGroup { + return LayoutInflater.from(context) + .inflate(R.layout.people_space_activity, /* root= */ null) as ViewGroup + } + + /** Bind [view] to [viewModel]. */ + @JvmStatic + fun bind( + view: ViewGroup, + viewModel: PeopleViewModel, + lifecycleOwner: LifecycleOwner, + onFinish: () -> Unit, + ) { + // Call [onFinish] this activity when the ViewModel tells us so. + lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.repeatOnLifecycle(CREATED) { + viewModel.isFinished.collect { isFinished -> + if (isFinished) { + viewModel.clearIsFinished() + onFinish() + } + } + } + } + + // Start collecting the UI data once the Activity is STARTED. + lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + combine( + viewModel.priorityTiles, + viewModel.recentTiles, + ) { priority, recent -> + priority to recent + } + .collect { (priorityTiles, recentTiles) -> + if (priorityTiles.isNotEmpty() || recentTiles.isNotEmpty()) { + setConversationsContent( + view, + priorityTiles, + recentTiles, + viewModel::onTileClicked, + ) + } else { + setNoConversationsContent(view) + } + } + } + } + + // Make sure to refresh the tiles/conversations when the Activity is resumed, so that it + // updates them when going back to the Activity after leaving it. + lifecycleOwner.lifecycleScope.launch { + lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { + viewModel.onTileRefreshRequested() + } + } + } + + private fun setNoConversationsContent(view: ViewGroup) { + // This should never happen. + if (view.childCount > 1) { + error("view has ${view.childCount} children, it should have maximum 1") + } + + // The static content for no conversations is already shown. + if (view.findViewById<View>(R.id.top_level_no_conversations) != null) { + return + } + + // If we were showing the content with conversations earlier, remove it. + if (view.childCount == 1) { + view.removeViewAt(0) + } + + val context = view.context + val noConversationsView = + LayoutInflater.from(context) + .inflate(R.layout.people_space_activity_no_conversations, /* root= */ view) + + // The Tile preview has colorBackground as its background. Change it so it's different than + // the activity's background. + val item = noConversationsView.findViewById<LinearLayout>(android.R.id.background) + val shape = item.background as GradientDrawable + val ta = + context.theme.obtainStyledAttributes( + intArrayOf(com.android.internal.R.attr.colorSurface) + ) + shape.setColor(ta.getColor(0, Color.WHITE)) + ta.recycle() + } + + private fun setConversationsContent( + view: ViewGroup, + priorityTiles: List<PeopleTileViewModel>, + recentTiles: List<PeopleTileViewModel>, + onTileClicked: (PeopleTileViewModel) -> Unit, + ) { + // This should never happen. + if (view.childCount > 1) { + error("view has ${view.childCount} children, it should have maximum 1") + } + + // Inflate the content with conversations, if it's not already. + if (view.findViewById<View>(R.id.top_level_with_conversations) == null) { + // If we were showing the content without conversations earlier, remove it. + if (view.childCount == 1) { + view.removeViewAt(0) + } + + LayoutInflater.from(view.context) + .inflate(R.layout.people_space_activity_with_conversations, /* root= */ view) + } + + // TODO(b/193782241): Replace the NestedScrollView + 2x LinearLayout from this layout into a + // single RecyclerView once this screen is tested by screenshot tests. Introduce a + // PeopleSpaceTileViewBinder that will properly create and bind the View associated to a + // PeopleSpaceTileViewModel (and remove the PeopleSpaceTileView class). + val conversationsView = view.requireViewById<View>(R.id.top_level_with_conversations) + setTileViews( + conversationsView, + R.id.priority, + R.id.priority_tiles, + priorityTiles, + onTileClicked, + ) + + setTileViews( + conversationsView, + R.id.recent, + R.id.recent_tiles, + recentTiles, + onTileClicked, + ) + } + + /** Sets a [PeopleSpaceTileView]s for each conversation. */ + private fun setTileViews( + root: View, + tilesListId: Int, + tilesId: Int, + tiles: List<PeopleTileViewModel>, + onTileClicked: (PeopleTileViewModel) -> Unit, + ) { + // Remove any previously added tile. + // TODO(b/193782241): Once this list is a big RecyclerView, set the current list and use + // DiffUtil to do as less addView/removeView as possible. + val layout = root.requireViewById<ViewGroup>(tilesId) + layout.removeAllViews() + layout.outlineProvider = ViewOutlineProvider + + val tilesListView = root.requireViewById<LinearLayout>(tilesListId) + if (tiles.isEmpty()) { + tilesListView.visibility = View.GONE + return + } + tilesListView.visibility = View.VISIBLE + + // Add each tile. + tiles.forEachIndexed { i, tile -> + val tileView = + PeopleSpaceTileView(root.context, layout, tile.key.shortcutId, i == tiles.size - 1) + bindTileView(tileView, tile, onTileClicked) + } + } + + /** Sets [tileView] with the data in [conversation]. */ + private fun bindTileView( + tileView: PeopleSpaceTileView, + tile: PeopleTileViewModel, + onTileClicked: (PeopleTileViewModel) -> Unit, + ) { + try { + tileView.setName(tile.username) + tileView.setPersonIcon(tile.icon) + tileView.setOnClickListener { onTileClicked(tile) } + } catch (e: Exception) { + Log.e(TAG, "Couldn't retrieve shortcut information", e) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleTileViewModel.kt new file mode 100644 index 000000000000..40205ce9424a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleTileViewModel.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.people.ui.viewmodel + +import android.graphics.Bitmap +import com.android.systemui.people.widget.PeopleTileKey + +/** Models UI state for a single tile/conversation. */ +data class PeopleTileViewModel( + val key: PeopleTileKey, + val icon: Bitmap, + val username: String?, +) diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt new file mode 100644 index 000000000000..17de991588b8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/people/ui/viewmodel/PeopleViewModel.kt @@ -0,0 +1,149 @@ +/* + * 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.people.ui.viewmodel + +import android.appwidget.AppWidgetManager.INVALID_APPWIDGET_ID +import android.content.Context +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.android.systemui.R +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.people.PeopleSpaceUtils +import com.android.systemui.people.PeopleTileViewHelper +import com.android.systemui.people.data.model.PeopleTileModel +import com.android.systemui.people.data.repository.PeopleTileRepository +import com.android.systemui.people.data.repository.PeopleWidgetRepository +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow + +/** + * Models UI state for the people space, allowing the user to select which conversation should be + * associated to a new or existing Conversation widget. + */ +class PeopleViewModel( + @Application private val context: Context, + private val tileRepository: PeopleTileRepository, + private val widgetRepository: PeopleWidgetRepository, +) : ViewModel() { + /** + * The list of the priority tiles/conversations. + * + * Important: Even though this is a Flow, the underlying API used to populate this Flow is not + * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles. + */ + private val _priorityTiles = MutableStateFlow(priorityTiles()) + val priorityTiles: Flow<List<PeopleTileViewModel>> = _priorityTiles + + /** + * The list of the priority tiles/conversations. + * + * Important: Even though this is a Flow, the underlying API used to populate this Flow is not + * reactive and you have to manually call [onTileRefreshRequested] to refresh the tiles. + */ + private val _recentTiles = MutableStateFlow(recentTiles()) + val recentTiles: Flow<List<PeopleTileViewModel>> = _recentTiles + + /** The ID of the widget currently being edited/added. */ + private val _appWidgetId = MutableStateFlow(INVALID_APPWIDGET_ID) + val appWidgetId: StateFlow<Int> = _appWidgetId + + /** Whether the user journey is complete. */ + private val _isFinished = MutableStateFlow(false) + val isFinished: StateFlow<Boolean> = _isFinished + + /** Refresh the [priorityTiles] and [recentTiles]. */ + fun onTileRefreshRequested() { + _priorityTiles.value = priorityTiles() + _recentTiles.value = recentTiles() + } + + /** Called when the [appWidgetId] should be changed to [widgetId]. */ + fun onWidgetIdChanged(widgetId: Int) { + _appWidgetId.value = widgetId + } + + /** Clear [isFinished], setting it to false. */ + fun clearIsFinished() { + _isFinished.value = false + } + + /** Called when a tile is clicked. */ + fun onTileClicked(tile: PeopleTileViewModel) { + if (PeopleSpaceUtils.DEBUG) { + Log.d( + TAG, + "Put ${tile.username}'s shortcut ID: ${tile.key.shortcutId} for widget ID: " + + _appWidgetId.value + ) + } + widgetRepository.setWidgetTile(_appWidgetId.value, tile.key) + _isFinished.value = true + } + + private fun priorityTiles(): List<PeopleTileViewModel> { + return try { + tileRepository.priorityTiles().map { it.toViewModel() } + } catch (e: Exception) { + Log.e(TAG, "Couldn't retrieve priority conversations", e) + emptyList() + } + } + + private fun recentTiles(): List<PeopleTileViewModel> { + return try { + tileRepository.recentTiles().map { it.toViewModel() } + } catch (e: Exception) { + Log.e(TAG, "Couldn't retrieve recent conversations", e) + emptyList() + } + } + + private fun PeopleTileModel.toViewModel(): PeopleTileViewModel { + val icon = + PeopleTileViewHelper.getPersonIconBitmap( + context, + this, + PeopleTileViewHelper.getSizeInDp( + context, + R.dimen.avatar_size_for_medium, + context.resources.displayMetrics.density, + ) + ) + return PeopleTileViewModel(key, icon, username) + } + + /** The Factory that should be used to create a [PeopleViewModel]. */ + class Factory + @Inject + constructor( + @Application private val context: Context, + private val tileRepository: PeopleTileRepository, + private val widgetRepository: PeopleWidgetRepository, + ) : ViewModelProvider.Factory { + override fun <T : ViewModel> create(modelClass: Class<T>): T { + check(modelClass == PeopleViewModel::class.java) + return PeopleViewModel(context, tileRepository, widgetRepository) as T + } + } + + companion object { + private const val TAG = "PeopleSpaceViewModel" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java index e1b97a454c5d..20c6c556706e 100644 --- a/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/widget/LaunchConversationActivity.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.LauncherApps; import android.os.Bundle; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; @@ -34,6 +35,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.people.PeopleSpaceUtils; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -43,6 +45,7 @@ import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubble; import java.util.Optional; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -58,6 +61,7 @@ public class LaunchConversationActivity extends Activity { private boolean mIsForTesting; private IStatusBarService mIStatusBarService; private CommandQueue mCommandQueue; + private Executor mBgExecutor; private Bubble mBubble; private NotificationEntry mEntryToBubble; @@ -67,7 +71,8 @@ public class LaunchConversationActivity extends Activity { CommonNotifCollection commonNotifCollection, Optional<BubblesManager> bubblesManagerOptional, UserManager userManager, - CommandQueue commandQueue + CommandQueue commandQueue, + @Background Executor bgExecutor ) { super(); mVisibilityProvider = visibilityProvider; @@ -91,6 +96,7 @@ public class LaunchConversationActivity extends Activity { mCommandQueue.removeCallback(this); } }); + mBgExecutor = bgExecutor; } @Override @@ -172,34 +178,36 @@ public class LaunchConversationActivity extends Activity { return; } - try { - if (mIStatusBarService == null || mCommonNotifCollection == null) { - if (DEBUG) { - Log.d(TAG, "Skipping clear notification: null services, key: " + notifKey); - } - return; + if (mIStatusBarService == null || mCommonNotifCollection == null) { + if (DEBUG) { + Log.d(TAG, "Skipping clear notification: null services, key: " + notifKey); } + return; + } - NotificationEntry entry = mCommonNotifCollection.getEntry(notifKey); - if (entry == null || entry.getRanking() == null) { - if (DEBUG) { - Log.d(TAG, "Skipping clear notification: NotificationEntry or its Ranking" - + " is null, key: " + notifKey); - } - return; + NotificationEntry entry = mCommonNotifCollection.getEntry(notifKey); + if (entry == null || entry.getRanking() == null) { + if (DEBUG) { + Log.d(TAG, "Skipping clear notification: NotificationEntry or its Ranking" + + " is null, key: " + notifKey); } + return; + } - NotificationVisibility notifVisibility = mVisibilityProvider.obtain(entry, true); - int rank = notifVisibility.rank; + NotificationVisibility notifVisibility = mVisibilityProvider.obtain(entry, true); + int rank = notifVisibility.rank; - if (DEBUG) Log.d(TAG, "Clearing notification, key: " + notifKey + ", rank: " + rank); - mIStatusBarService.onNotificationClear( - packageName, userHandle.getIdentifier(), notifKey, - NotificationStats.DISMISSAL_OTHER, - NotificationStats.DISMISS_SENTIMENT_POSITIVE, notifVisibility); - } catch (Exception e) { - Log.e(TAG, "Exception cancelling notification:" + e); - } + if (DEBUG) Log.d(TAG, "Clearing notification, key: " + notifKey + ", rank: " + rank); + mBgExecutor.execute(() -> { + try { + mIStatusBarService.onNotificationClear( + packageName, userHandle.getIdentifier(), notifKey, + NotificationStats.DISMISSAL_OTHER, + NotificationStats.DISMISS_SENTIMENT_POSITIVE, notifVisibility); + } catch (RemoteException e) { + Log.e(TAG, "Exception cancelling notification:" + e); + } + }); } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index fcafeada9d9a..0697133a02f9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -15,6 +15,7 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; @@ -626,6 +627,16 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { } } + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (mAdapter != null && mAdapter.getCount() > 0) { + event.setItemCount(mAdapter.getCount()); + event.setFromIndex(getCurrentPageNumber()); + event.setToIndex(getCurrentPageNumber()); + } + } + private static Animator setupBounceAnimator(View view, int ordinal) { view.setAlpha(0f); view.setScaleX(0f); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index ccec80db3ea5..86d4fa3002fc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -56,7 +56,6 @@ import dagger.Lazy; /** Quick settings tile: Airplane mode **/ public class AirplaneModeTile extends QSTileImpl<BooleanState> { - private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_airplane); private final SettingObserver mSetting; private final BroadcastDispatcher mBroadcastDispatcher; private final Lazy<ConnectivityManager> mLazyConnectivityManager; @@ -129,11 +128,8 @@ public class AirplaneModeTile extends QSTileImpl<BooleanState> { final boolean airplaneMode = value != 0; state.value = airplaneMode; state.label = mContext.getString(R.string.airplane_mode); - state.icon = mIcon; - if (state.slash == null) { - state.slash = new SlashState(); - } - state.slash.isSlashed = !airplaneMode; + state.icon = ResourceIcon.get(state.value + ? R.drawable.qs_airplane_icon_on : R.drawable.qs_airplane_icon_off); state.state = airplaneMode ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; state.contentDescription = state.label; state.expandedAccessibilityClassName = Switch.class.getName(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java index 1004fcae3827..ee49b294dfcd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java @@ -57,8 +57,6 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements private boolean mCharging; private boolean mPluggedIn; - private Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_battery_saver); - @Inject public BatterySaverTile( QSHost host, @@ -145,7 +143,9 @@ public class BatterySaverTile extends QSTileImpl<BooleanState> implements protected void handleUpdateState(BooleanState state, Object arg) { state.state = mPluggedIn ? Tile.STATE_UNAVAILABLE : mPowerSave ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; - state.icon = mIcon; + state.icon = ResourceIcon.get(mPowerSave + ? R.drawable.qs_battery_saver_icon_on + : R.drawable.qs_battery_saver_icon_off); state.label = mContext.getString(R.string.battery_detail_switch_title); state.secondaryLabel = ""; state.contentDescription = state.label; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java index fa2d4447f26d..ee41f1d1077d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java @@ -72,9 +72,9 @@ public class CameraToggleTile extends SensorPrivacyToggleTile { @Override public @DrawableRes int getIconRes(boolean isBlocked) { if (isBlocked) { - return com.android.internal.R.drawable.ic_camera_blocked; + return R.drawable.qs_camera_access_icon_off; } else { - return com.android.internal.R.drawable.ic_camera_allowed; + return R.drawable.qs_camera_access_icon_on; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 3dcfbc04c463..8b7f53fa5a3f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -226,16 +226,15 @@ public class DndTile extends QSTileImpl<BooleanState> { if (mController == null) return; final int zen = arg instanceof Integer ? (Integer) arg : mController.getZen(); final boolean newValue = zen != ZEN_MODE_OFF; - final boolean valueChanged = state.value != newValue; - if (state.slash == null) state.slash = new SlashState(); state.dualTarget = true; state.value = newValue; state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; - state.slash.isSlashed = !state.value; + state.icon = ResourceIcon.get(state.value + ? R.drawable.qs_dnd_icon_on + : R.drawable.qs_dnd_icon_off); state.label = getTileLabel(); state.secondaryLabel = TextUtils.emptyIfNull(ZenModeConfig.getDescription(mContext, zen != Global.ZEN_MODE_OFF, mController.getConfig(), false)); - state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_dnd); checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_ADJUST_VOLUME); // Keeping the secondaryLabel in contentDescription instead of stateDescription is easier // to understand. diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index fc93f44a44aa..9466a694ea1b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -48,8 +48,6 @@ import javax.inject.Inject; /** Quick settings tile: Location **/ public class LocationTile extends QSTileImpl<BooleanState> { - private final Icon mIcon = ResourceIcon.get(R.drawable.ic_location); - private final LocationController mController; private final KeyguardStateController mKeyguard; private final Callback mCallback = new Callback(); @@ -119,8 +117,8 @@ public class LocationTile extends QSTileImpl<BooleanState> { if (state.disabledByPolicy == false) { checkIfRestrictionEnforcedByAdminOnly(state, UserManager.DISALLOW_CONFIG_LOCATION); } - state.icon = mIcon; - state.slash.isSlashed = !state.value; + state.icon = ResourceIcon.get(state.value + ? R.drawable.qs_location_icon_on : R.drawable.qs_location_icon_off); state.label = mContext.getString(R.string.quick_settings_location_label); state.contentDescription = state.label; state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java index f4f0b2cdc432..e54709562c10 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java @@ -72,9 +72,9 @@ public class MicrophoneToggleTile extends SensorPrivacyToggleTile { @Override public @DrawableRes int getIconRes(boolean isBlocked) { if (isBlocked) { - return com.android.internal.R.drawable.ic_mic_blocked; + return R.drawable.qs_mic_access_off; } else { - return com.android.internal.R.drawable.ic_mic_allowed; + return R.drawable.qs_mic_access_on; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt index 260a3714a368..bdcc6b0b2a57 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt @@ -128,15 +128,16 @@ class UserSwitchDialogController @VisibleForTesting constructor( private val animateFrom: Dialog, private val dialogLaunchAnimator: DialogLaunchAnimator ) : DialogInterface by animateFrom, DialogShower { - override fun showDialog(dialog: Dialog) { + override fun showDialog(dialog: Dialog, cuj: DialogCuj) { dialogLaunchAnimator.showFromDialog( dialog, - animateFrom = animateFrom + animateFrom = animateFrom, + cuj ) } } interface DialogShower : DialogInterface { - fun showDialog(dialog: Dialog) + fun showDialog(dialog: Dialog, cuj: DialogCuj) } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 6c58c459c2c2..438236d6a63d 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -51,7 +51,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; -import android.graphics.Bitmap; import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; @@ -97,6 +96,7 @@ import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.navigationbar.buttons.KeyButtonView; import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; import com.android.systemui.settings.CurrentUserTracker; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.recents.model.Task; @@ -105,7 +105,6 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.phone.CentralSurfaces; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBarWindowCallback; import com.android.systemui.statusbar.policy.CallbackController; import com.android.wm.shell.back.BackAnimation; diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java index 5bb3413595ba..a837cbb8a50e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java @@ -29,6 +29,7 @@ import android.graphics.drawable.Icon; import android.media.MediaRecorder; import android.net.Uri; import android.os.Bundle; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; @@ -40,6 +41,8 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.LongRunning; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.screenrecord.ScreenMediaRecorder.ScreenMediaRecorderListener; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; @@ -51,9 +54,10 @@ import javax.inject.Inject; /** * A service which records the device screen and optionally microphone input. */ -public class RecordingService extends Service implements MediaRecorder.OnInfoListener { +public class RecordingService extends Service implements ScreenMediaRecorderListener { public static final int REQUEST_CODE = 2; + private static final int USER_ID_NOT_SPECIFIED = -1; private static final int NOTIFICATION_RECORDING_ID = 4274; private static final int NOTIFICATION_PROCESSING_ID = 4275; private static final int NOTIFICATION_VIEW_ID = 4273; @@ -73,6 +77,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis private final RecordingController mController; private final KeyguardDismissUtil mKeyguardDismissUtil; + private final Handler mMainHandler; private ScreenRecordingAudioSource mAudioSource; private boolean mShowTaps; private boolean mOriginalShowTaps; @@ -84,10 +89,12 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis @Inject public RecordingService(RecordingController controller, @LongRunning Executor executor, - UiEventLogger uiEventLogger, NotificationManager notificationManager, + @Main Handler handler, UiEventLogger uiEventLogger, + NotificationManager notificationManager, UserContextProvider userContextTracker, KeyguardDismissUtil keyguardDismissUtil) { mController = controller; mLongExecutor = executor; + mMainHandler = handler; mUiEventLogger = uiEventLogger; mNotificationManager = notificationManager; mUserContextTracker = userContextTracker; @@ -138,6 +145,7 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis mRecorder = new ScreenMediaRecorder( mUserContextTracker.getUserContext(), + mMainHandler, currentUserId, mAudioSource, this @@ -166,14 +174,8 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis } // Check user ID - we may be getting a stop intent after user switch, in which case // we want to post the notifications for that user, which is NOT current user - int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (userId == -1) { - userId = mUserContextTracker.getUserContext().getUserId(); - } - Log.d(TAG, "notifying for user " + userId); - stopRecording(userId); - mNotificationManager.cancel(NOTIFICATION_RECORDING_ID); - stopSelf(); + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_ID_NOT_SPECIFIED); + stopService(userId); break; case ACTION_SHARE: @@ -378,15 +380,39 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis return builder.build(); } - private void stopRecording(int userId) { + private void stopService() { + stopService(USER_ID_NOT_SPECIFIED); + } + + private void stopService(int userId) { + if (userId == USER_ID_NOT_SPECIFIED) { + userId = mUserContextTracker.getUserContext().getUserId(); + } + Log.d(TAG, "notifying for user " + userId); setTapsVisible(mOriginalShowTaps); if (getRecorder() != null) { - getRecorder().end(); - saveRecording(userId); + try { + getRecorder().end(); + saveRecording(userId); + } catch (RuntimeException exception) { + // RuntimeException could happen if the recording stopped immediately after starting + // let's release the recorder and delete all temporary files in this case + getRecorder().release(); + showErrorToast(R.string.screenrecord_start_error); + Log.e(TAG, "stopRecording called, but there was an error when ending" + + "recording"); + exception.printStackTrace(); + } catch (Throwable throwable) { + // Something unexpected happen, SystemUI will crash but let's delete + // the temporary files anyway + getRecorder().release(); + throw new RuntimeException(throwable); + } } else { Log.e(TAG, "stopRecording called, but recorder was null"); } updateState(false); + stopSelf(); } private void saveRecording(int userId) { @@ -446,4 +472,12 @@ public class RecordingService extends Service implements MediaRecorder.OnInfoLis Log.d(TAG, "Media recorder info: " + what); onStartCommand(getStopIntent(this), 0, 0); } + + @Override + public void onStopped() { + if (mController.isRecording()) { + Log.d(TAG, "Stopping recording because the system requested the stop"); + stopService(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 2133cf63d1c3..d098b4b3442a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -40,6 +40,7 @@ import android.media.projection.IMediaProjectionManager; import android.media.projection.MediaProjection; import android.media.projection.MediaProjectionManager; import android.net.Uri; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -51,16 +52,19 @@ import android.view.Surface; import android.view.WindowManager; import java.io.File; +import java.io.Closeable; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.List; /** * Recording screen and mic/internal audio */ -public class ScreenMediaRecorder { +public class ScreenMediaRecorder extends MediaProjection.Callback { private static final int TOTAL_NUM_TRACKS = 1; private static final int VIDEO_FRAME_RATE = 30; private static final int VIDEO_FRAME_RATE_TO_RESOLUTION_RATIO = 6; @@ -81,14 +85,16 @@ public class ScreenMediaRecorder { private ScreenRecordingMuxer mMuxer; private ScreenInternalAudioRecorder mAudio; private ScreenRecordingAudioSource mAudioSource; + private final Handler mHandler; private Context mContext; - MediaRecorder.OnInfoListener mListener; + ScreenMediaRecorderListener mListener; - public ScreenMediaRecorder(Context context, + public ScreenMediaRecorder(Context context, Handler handler, int user, ScreenRecordingAudioSource audioSource, - MediaRecorder.OnInfoListener listener) { + ScreenMediaRecorderListener listener) { mContext = context; + mHandler = handler; mUser = user; mListener = listener; mAudioSource = audioSource; @@ -105,6 +111,7 @@ public class ScreenMediaRecorder { IBinder projection = proj.asBinder(); mMediaProjection = new MediaProjection(mContext, IMediaProjection.Stub.asInterface(projection)); + mMediaProjection.registerCallback(this, mHandler); File cacheDir = mContext.getCacheDir(); cacheDir.mkdirs(); @@ -162,10 +169,15 @@ public class ScreenMediaRecorder { metrics.densityDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mInputSurface, - null, - null); - - mMediaRecorder.setOnInfoListener(mListener); + new VirtualDisplay.Callback() { + @Override + public void onStopped() { + onStop(); + } + }, + mHandler); + + mMediaRecorder.setOnInfoListener((mr, what, extra) -> mListener.onInfo(mr, what, extra)); if (mAudioSource == INTERNAL || mAudioSource == MIC_AND_INTERNAL) { mTempAudioFile = File.createTempFile("temp", ".aac", @@ -259,21 +271,34 @@ public class ScreenMediaRecorder { } /** - * End screen recording + * End screen recording, throws an exception if stopping recording failed */ - void end() { - mMediaRecorder.stop(); - mMediaRecorder.release(); - mInputSurface.release(); - mVirtualDisplay.release(); - mMediaProjection.stop(); + void end() throws IOException { + Closer closer = new Closer(); + + // MediaRecorder might throw RuntimeException if stopped immediately after starting + // We should remove the recording in this case as it will be invalid + closer.register(mMediaRecorder::stop); + closer.register(mMediaRecorder::release); + closer.register(mInputSurface::release); + closer.register(mVirtualDisplay::release); + closer.register(mMediaProjection::stop); + closer.register(this::stopInternalAudioRecording); + + closer.close(); + mMediaRecorder = null; mMediaProjection = null; - stopInternalAudioRecording(); Log.d(TAG, "end recording"); } + @Override + public void onStop() { + Log.d(TAG, "The system notified about stopping the projection"); + mListener.onStopped(); + } + private void stopInternalAudioRecording() { if (mAudioSource == INTERNAL || mAudioSource == MIC_AND_INTERNAL) { mAudio.end(); @@ -337,6 +362,18 @@ public class ScreenMediaRecorder { } /** + * Release the resources without saving the data + */ + protected void release() { + if (mTempVideoFile != null) { + mTempVideoFile.delete(); + } + if (mTempAudioFile != null) { + mTempAudioFile.delete(); + } + } + + /** * Object representing the recording */ public class SavedRecording { @@ -362,4 +399,66 @@ public class ScreenMediaRecorder { return mThumbnailBitmap; } } + + interface ScreenMediaRecorderListener { + /** + * Called to indicate an info or a warning during recording. + * See {@link MediaRecorder.OnInfoListener} for the full description. + */ + void onInfo(MediaRecorder mr, int what, int extra); + + /** + * Called when the recording stopped by the system. + * For example, this might happen when doing partial screen sharing of an app + * and the app that is being captured is closed. + */ + void onStopped(); + } + + /** + * Allows to register multiple {@link Closeable} objects and close them all by calling + * {@link Closer#close}. If there is an exception thrown during closing of one + * of the registered closeables it will continue trying closing the rest closeables. + * If there are one or more exceptions thrown they will be re-thrown at the end. + * In case of multiple exceptions only the first one will be thrown and all the rest + * will be printed. + */ + private static class Closer implements Closeable { + private final List<Closeable> mCloseables = new ArrayList<>(); + + void register(Closeable closeable) { + mCloseables.add(closeable); + } + + @Override + public void close() throws IOException { + Throwable throwable = null; + + for (int i = 0; i < mCloseables.size(); i++) { + Closeable closeable = mCloseables.get(i); + + try { + closeable.close(); + } catch (Throwable e) { + if (throwable == null) { + throwable = e; + } else { + e.printStackTrace(); + } + } + } + + if (throwable != null) { + if (throwable instanceof IOException) { + throw (IOException) throwable; + } + + if (throwable instanceof RuntimeException) { + throw (RuntimeException) throwable; + } + + throw (Error) throwable; + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java index daaa897374cb..814b8e90e0dd 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionProxyReceiver.java @@ -102,7 +102,7 @@ public class ActionProxyReceiver extends BroadcastReceiver { ? ACTION_TYPE_EDIT : ACTION_TYPE_SHARE; mScreenshotSmartActions.notifyScreenshotAction( - context, intent.getStringExtra(EXTRA_ID), actionType, false, null); + intent.getStringExtra(EXTRA_ID), actionType, false, null); } } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java index 8d44205bef30..e0346f2e2a98 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/DeleteScreenshotReceiver.java @@ -62,7 +62,7 @@ public class DeleteScreenshotReceiver extends BroadcastReceiver { }); if (intent.getBooleanExtra(EXTRA_SMART_ACTIONS_ENABLED, false)) { mScreenshotSmartActions.notifyScreenshotAction( - context, intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false, null); + intent.getStringExtra(EXTRA_ID), ACTION_TYPE_DELETE, false, null); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java index 60123ab97fd7..6224e1bf2414 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/hidedisplaycutout/HideDisplayCutout.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ReferenceScreenshotModule.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 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,23 +14,19 @@ * limitations under the License. */ -package com.android.wm.shell.hidedisplaycutout; +package com.android.systemui.screenshot; -import android.content.res.Configuration; - -import androidx.annotation.NonNull; - -import com.android.wm.shell.common.annotations.ExternalThread; - -import java.io.PrintWriter; +import dagger.Module; +import dagger.Provides; /** - * Interface to engage hide display cutout feature. + * */ -@ExternalThread -public interface HideDisplayCutout { - /** - * Notifies {@link Configuration} changed. - */ - void onConfigurationChanged(Configuration newConfig); +@Module +public interface ReferenceScreenshotModule { + /** */ + @Provides + static ScreenshotNotificationSmartActionsProvider providesScrnshtNotifSmartActionsProvider() { + return new ScreenshotNotificationSmartActionsProvider(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java index 50ee1f7ba97a..f248d6913878 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java @@ -38,7 +38,6 @@ import android.graphics.drawable.Icon; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; -import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; @@ -49,7 +48,6 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.R; -import com.android.systemui.SystemUIFactory; import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; import com.google.common.util.concurrent.ListenableFuture; @@ -89,7 +87,10 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { SaveImageInBackgroundTask(Context context, ImageExporter exporter, ScreenshotSmartActions screenshotSmartActions, ScreenshotController.SaveImageInBackgroundData data, - Supplier<ActionTransition> sharedElementTransition) { + Supplier<ActionTransition> sharedElementTransition, + ScreenshotNotificationSmartActionsProvider + screenshotNotificationSmartActionsProvider + ) { mContext = context; mScreenshotSmartActions = screenshotSmartActions; mImageData = new ScreenshotController.SavedImageData(); @@ -103,15 +104,7 @@ class SaveImageInBackgroundTask extends AsyncTask<Void, Void, Void> { // Initialize screenshot notification smart actions provider. mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI, SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true); - if (mSmartActionsEnabled) { - mSmartActionsProvider = - SystemUIFactory.getInstance() - .createScreenshotNotificationSmartActionsProvider( - context, THREAD_POOL_EXECUTOR, new Handler()); - } else { - // If smart actions is not enabled use empty implementation. - mSmartActionsProvider = new ScreenshotNotificationSmartActionsProvider(); - } + mSmartActionsProvider = screenshotNotificationSmartActionsProvider; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index c213f192291a..89a15f65e98f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -263,6 +263,8 @@ public class ScreenshotController { private final ScrollCaptureController mScrollCaptureController; private final LongScreenshotData mLongScreenshotHolder; private final boolean mIsLowRamDevice; + private final ScreenshotNotificationSmartActionsProvider + mScreenshotNotificationSmartActionsProvider; private final TimeoutHandler mScreenshotHandler; private ScreenshotView mScreenshotView; @@ -298,7 +300,9 @@ public class ScreenshotController { LongScreenshotData longScreenshotHolder, ActivityManager activityManager, TimeoutHandler timeoutHandler, - BroadcastSender broadcastSender) { + BroadcastSender broadcastSender, + ScreenshotNotificationSmartActionsProvider screenshotNotificationSmartActionsProvider + ) { mScreenshotSmartActions = screenshotSmartActions; mNotificationsController = screenshotNotificationsController; mScrollCaptureClient = scrollCaptureClient; @@ -308,6 +312,7 @@ public class ScreenshotController { mScrollCaptureController = scrollCaptureController; mLongScreenshotHolder = longScreenshotHolder; mIsLowRamDevice = activityManager.isLowRamDevice(); + mScreenshotNotificationSmartActionsProvider = screenshotNotificationSmartActionsProvider; mBgExecutor = Executors.newSingleThreadExecutor(); mBroadcastSender = broadcastSender; @@ -956,7 +961,8 @@ public class ScreenshotController { } mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter, - mScreenshotSmartActions, data, getActionTransitionSupplier()); + mScreenshotSmartActions, data, getActionTransitionSupplier(), + mScreenshotNotificationSmartActionsProvider); mSaveInBgTask.execute(); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java index 0527818135dd..68b46d2b7525 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotSmartActions.java @@ -16,8 +16,6 @@ package com.android.systemui.screenshot; -import static android.os.AsyncTask.THREAD_POOL_EXECUTOR; - import static com.android.systemui.screenshot.LogConfig.DEBUG_ACTIONS; import static com.android.systemui.screenshot.LogConfig.logTag; import static com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider.ScreenshotSmartActionType; @@ -25,17 +23,14 @@ import static com.android.systemui.screenshot.ScreenshotNotificationSmartActions import android.app.ActivityManager; import android.app.Notification; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; -import android.os.Handler; import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.SystemUIFactory; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -46,6 +41,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import javax.inject.Inject; +import javax.inject.Provider; /** * Collects the static functions for retrieving and acting on smart actions. @@ -53,9 +49,17 @@ import javax.inject.Inject; @SysUISingleton public class ScreenshotSmartActions { private static final String TAG = logTag(ScreenshotSmartActions.class); + private final Provider<ScreenshotNotificationSmartActionsProvider> + mScreenshotNotificationSmartActionsProviderProvider; @Inject - public ScreenshotSmartActions() {} + public ScreenshotSmartActions( + Provider<ScreenshotNotificationSmartActionsProvider> + screenshotNotificationSmartActionsProviderProvider + ) { + mScreenshotNotificationSmartActionsProviderProvider = + screenshotNotificationSmartActionsProviderProvider; + } @VisibleForTesting CompletableFuture<List<Notification.Action>> getSmartActionsFuture( @@ -165,12 +169,11 @@ public class ScreenshotSmartActions { } } - void notifyScreenshotAction(Context context, String screenshotId, String action, + void notifyScreenshotAction(String screenshotId, String action, boolean isSmartAction, Intent intent) { try { ScreenshotNotificationSmartActionsProvider provider = - SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider( - context, THREAD_POOL_EXECUTOR, new Handler()); + mScreenshotNotificationSmartActionsProviderProvider.get(); if (DEBUG_ACTIONS) { Log.d(TAG, String.format("%s notifyAction: %s id=%s, isSmartAction=%b", provider.getClass(), action, screenshotId, isSmartAction)); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java index f703058f4a0f..45af1874e9db 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/SmartActionsReceiver.java @@ -60,7 +60,7 @@ public class SmartActionsReceiver extends BroadcastReceiver { } mScreenshotSmartActions.notifyScreenshotAction( - context, intent.getStringExtra(EXTRA_ID), actionType, true, + intent.getStringExtra(EXTRA_ID), actionType, true, pendingIntent.getIntent()); } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt new file mode 100644 index 000000000000..aa218dbfdd43 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt @@ -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 com.android.systemui.settings + +import android.content.Context +import android.content.SharedPreferences +import java.io.File + +/** + * Interface for retrieving file paths for file storage of system and secondary users. + */ +interface UserFileManager { + /** + * Return the file based on current user. + */ + fun getFile(fileName: String, userId: Int): File + /** + * Get shared preferences from user. + */ + fun getSharedPreferences( + fileName: String, + @Context.PreferencesMode mode: Int, + userId: Int + ): SharedPreferences +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt new file mode 100644 index 000000000000..8c8f54fe9c3d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt @@ -0,0 +1,144 @@ +/* + * 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.settings + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.SharedPreferences +import android.os.Environment +import android.os.UserHandle +import android.os.UserManager +import android.util.Log +import androidx.annotation.VisibleForTesting +import com.android.systemui.CoreStartable +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.util.concurrency.DelayableExecutor +import java.io.File +import javax.inject.Inject + +/** + * Implementation for retrieving file paths for file storage of system and secondary users. + * Files lie in {File Directory}/UserFileManager/{User Id} for secondary user. + * For system user, we use the conventional {File Directory} + */ +@SysUISingleton +class UserFileManagerImpl @Inject constructor( + // Context of system process and system user. + val context: Context, + val userManager: UserManager, + val broadcastDispatcher: BroadcastDispatcher, + @Background val backgroundExecutor: DelayableExecutor +) : UserFileManager, CoreStartable(context) { + companion object { + private const val FILES = "files" + private const val SHARED_PREFS = "shared_prefs" + internal const val ID = "UserFileManager" + } + + private val broadcastReceiver = object : BroadcastReceiver() { + /** + * Listen to Intent.ACTION_USER_REMOVED to clear user data. + */ + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == Intent.ACTION_USER_REMOVED) { + clearDeletedUserData() + } + } + } + + /** + * Poll for user-specific directories to delete upon start up. + */ + override fun start() { + clearDeletedUserData() + val filter = IntentFilter().apply { + addAction(Intent.ACTION_USER_REMOVED) + } + broadcastDispatcher.registerReceiver(broadcastReceiver, filter, backgroundExecutor) + } + + /** + * Return the file based on current user. + */ + override fun getFile(fileName: String, userId: Int): File { + return if (UserHandle(userId).isSystem) { + Environment.buildPath( + context.filesDir, + fileName + ) + } else { + Environment.buildPath( + context.filesDir, + ID, + userId.toString(), + FILES, + fileName + ) + } + } + + /** + * Get shared preferences from user. + */ + override fun getSharedPreferences( + fileName: String, + @Context.PreferencesMode mode: Int, + userId: Int + ): SharedPreferences { + if (UserHandle(userId).isSystem) { + return context.getSharedPreferences(fileName, mode) + } + val secondaryUserDir = Environment.buildPath( + context.filesDir, + ID, + userId.toString(), + SHARED_PREFS, + fileName + ) + + return context.getSharedPreferences(secondaryUserDir, mode) + } + + /** + * Remove dirs for deleted users. + */ + @VisibleForTesting + internal fun clearDeletedUserData() { + backgroundExecutor.execute { + val file = Environment.buildPath(context.filesDir, ID) + if (!file.exists()) return@execute + val aliveUsers = userManager.aliveUsers.map { it.id.toString() } + val dirsToDelete = file.list().filter { !aliveUsers.contains(it) } + + dirsToDelete.forEach { dir -> + try { + val dirToDelete = Environment.buildPath( + file, + dir, + ) + dirToDelete.deleteRecursively() + } catch (e: Exception) { + Log.e(ID, "Deletion failed.", e) + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java index 7084d3ffc9ff..2f62e44ba4c4 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java +++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/MultiUserUtilsModule.java @@ -21,25 +21,28 @@ import android.content.Context; import android.os.Handler; import android.os.UserManager; +import com.android.systemui.CoreStartable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; import com.android.systemui.settings.UserContentResolverProvider; import com.android.systemui.settings.UserContextProvider; +import com.android.systemui.settings.UserFileManager; +import com.android.systemui.settings.UserFileManagerImpl; import com.android.systemui.settings.UserTracker; import com.android.systemui.settings.UserTrackerImpl; import dagger.Binds; import dagger.Module; import dagger.Provides; +import dagger.multibindings.ClassKey; +import dagger.multibindings.IntoMap; /** * Dagger Module for classes found within the com.android.systemui.settings package. */ @Module -public abstract class SettingsModule { - - +public abstract class MultiUserUtilsModule { @Binds @SysUISingleton abstract UserContextProvider bindUserContextProvider(UserTracker tracker); @@ -62,4 +65,12 @@ public abstract class SettingsModule { tracker.initialize(startingUser); return tracker; } + + @Binds + @IntoMap + @ClassKey(UserFileManagerImpl.class) + abstract CoreStartable bindUserFileManagerCoreStartable(UserFileManagerImpl sysui); + + @Binds + abstract UserFileManager bindUserFileManager(UserFileManagerImpl impl); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVCDownEventState.kt b/packages/SystemUI/src/com/android/systemui/shade/NPVCDownEventState.kt index d44a56942065..07e8b9fe3123 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVCDownEventState.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NPVCDownEventState.kt @@ -11,7 +11,7 @@ * 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 +package com.android.systemui.shade import android.view.MotionEvent import com.android.systemui.dump.DumpsysTableLogger diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEvents.kt b/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt index a385e22c1aff..ce9d89f89ae1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEvents.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone +package com.android.systemui.shade /** Provides certain notification panel events. */ interface NotifPanelEvents { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEventsModule.java b/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEventsModule.java index 2aaf6a5f4391..67723843086a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotifPanelEventsModule.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotifPanelEventsModule.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.shade; import com.android.systemui.dagger.SysUISingleton; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt index ff48755f750a..e0cd482166b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelUnfoldAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone +package com.android.systemui.shade import android.content.Context import android.view.ViewGroup diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java index d9ba494a4d63..e0997ff0f905 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.shade; import android.content.Context; import android.graphics.Canvas; @@ -25,6 +25,8 @@ import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; import com.android.systemui.R; +import com.android.systemui.statusbar.phone.PanelView; +import com.android.systemui.statusbar.phone.TapAgainView; public class NotificationPanelView extends PanelView { @@ -35,8 +37,8 @@ public class NotificationPanelView extends PanelView { */ public static final int FLING_EXPAND = 0; - static final String COUNTER_PANEL_OPEN = "panel_open"; - static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; + public static final String COUNTER_PANEL_OPEN = "panel_open"; + public static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs"; private int mCurrentPanelAlpha; private final Paint mAlphaPaint = new Paint(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 648bb0c0b8e6..bab92ba492c9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.shade; import static android.app.StatusBarManager.WINDOW_STATE_SHOWING; @@ -40,8 +40,6 @@ import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStat import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_OPENING; import static com.android.systemui.util.DumpUtilsKt.asIndenting; -import static java.lang.Float.isNaN; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; @@ -50,13 +48,13 @@ import android.app.ActivityManager; import android.app.Fragment; import android.app.StatusBarManager; import android.content.ContentResolver; -import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.ColorFilter; import android.graphics.Insets; import android.graphics.Paint; +import android.graphics.PixelFormat; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; @@ -79,6 +77,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; +import android.view.View.AccessibilityDelegate; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import android.view.ViewStub; @@ -141,6 +140,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.qrcodescanner.controller.QRCodeScannerController; import com.android.systemui.screenrecord.RecordingController; +import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.GestureRecorder; @@ -177,12 +177,35 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; +import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; +import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; +import com.android.systemui.statusbar.phone.KeyguardBouncer; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.KeyguardClockPositionAlgorithm; +import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController; +import com.android.systemui.statusbar.phone.LargeScreenShadeHeaderController; +import com.android.systemui.statusbar.phone.LockscreenGestureLogger; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; +import com.android.systemui.statusbar.phone.NotificationIconAreaController; +import com.android.systemui.statusbar.phone.PanelView; +import com.android.systemui.statusbar.phone.PanelViewController; +import com.android.systemui.statusbar.phone.PhoneStatusBarView; +import com.android.systemui.statusbar.phone.ScreenOffAnimationController; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; +import com.android.systemui.statusbar.phone.TapAgainViewController; +import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.phone.panelstate.PanelState; -import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -195,7 +218,6 @@ import com.android.systemui.util.Compile; import com.android.systemui.util.LargeScreenUtils; import com.android.systemui.util.ListenerSet; import com.android.systemui.util.Utils; -import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.util.time.SystemClock; import com.android.systemui.wallet.controller.QuickAccessWalletController; import com.android.wm.shell.animation.FlingAnimationUtils; @@ -207,14 +229,13 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; -import java.util.concurrent.Executor; import java.util.function.Consumer; import javax.inject.Inject; import javax.inject.Provider; @CentralSurfacesComponent.CentralSurfacesScope -public class NotificationPanelViewController extends PanelViewController { +public final class NotificationPanelViewController extends PanelViewController { private static final boolean DEBUG_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG); private static final boolean SPEW_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE); @@ -260,7 +281,8 @@ public class NotificationPanelViewController extends PanelViewController { private final ConfigurationListener mConfigurationListener = new ConfigurationListener(); private final SettingsChangeObserver mSettingsChangeObserver; - @VisibleForTesting final StatusBarStateListener mStatusBarStateListener = + @VisibleForTesting + final StatusBarStateListener mStatusBarStateListener = new StatusBarStateListener(); private final NotificationPanelView mView; private final VibratorHelper mVibratorHelper; @@ -337,17 +359,20 @@ public class NotificationPanelViewController extends PanelViewController { private KeyguardUserSwitcherController mKeyguardUserSwitcherController; private KeyguardStatusBarView mKeyguardStatusBar; private KeyguardStatusBarViewController mKeyguardStatusBarViewController; - @VisibleForTesting QS mQs; + @VisibleForTesting + QS mQs; private FrameLayout mQsFrame; - private QsFrameTranslateController mQsFrameTranslateController; + private final QsFrameTranslateController mQsFrameTranslateController; private KeyguardStatusViewController mKeyguardStatusViewController; - private LockIconViewController mLockIconViewController; + private final LockIconViewController mLockIconViewController; private NotificationsQuickSettingsContainer mNotificationContainerParent; - private NotificationsQSContainerController mNotificationsQSContainerController; + private final NotificationsQSContainerController mNotificationsQSContainerController; + private final Provider<KeyguardBottomAreaViewController> + mKeyguardBottomAreaViewControllerProvider; private boolean mAnimateNextPositionUpdate; private float mQuickQsHeaderHeight; - private ScreenOffAnimationController mScreenOffAnimationController; - private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; + private final ScreenOffAnimationController mScreenOffAnimationController; + private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; private int mTrackingPointer; private VelocityTracker mQsVelocityTracker; @@ -400,12 +425,6 @@ public class NotificationPanelViewController extends PanelViewController { private int mLargeScreenShadeHeaderHeight; private int mSplitShadeNotificationsScrimMarginBottom; - /** - * Vertical overlap allowed between the bottom of the notification shelf and - * the top of the lock icon or the under-display fingerprint sensor background. - */ - private int mShelfAndLockIconOverlap; - private final KeyguardClockPositionAlgorithm mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(); @@ -420,7 +439,8 @@ public class NotificationPanelViewController extends PanelViewController { * Determines if QS should be already expanded when expanding shade. * Used for split shade, two finger gesture as well as accessibility shortcut to QS. */ - @VisibleForTesting boolean mQsExpandImmediate; + @VisibleForTesting + boolean mQsExpandImmediate; private boolean mTwoFingerQsExpandPossible; private String mHeaderDebugInfo; @@ -442,14 +462,13 @@ public class NotificationPanelViewController extends PanelViewController { private int mNavigationBarBottomHeight; private boolean mExpandingFromHeadsUp; private boolean mCollapsedOnDown; - private int mPositionMinSideMargin; private boolean mClosingWithAlphaFadeOut; private boolean mHeadsUpAnimatingAway; private boolean mLaunchingAffordance; private final FalsingManager mFalsingManager; private final FalsingCollector mFalsingCollector; - private Runnable mHeadsUpExistenceChangedRunnable = () -> { + private final Runnable mHeadsUpExistenceChangedRunnable = () -> { setHeadsUpAnimatingAway(false); updatePanelExpansionAndVisibility(); }; @@ -459,9 +478,6 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mIsFullWidth; private boolean mBlockingExpansionForCurrentTouch; - // TODO (b/204204226): no longer needed once refactor is complete - private final boolean mUseCombinedQSHeaders; - /** * Following variables maintain state of events when input focus transfer may occur. */ @@ -505,10 +521,10 @@ public class NotificationPanelViewController extends PanelViewController { mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_OUT); private final AnimationProperties mPanelAlphaInPropertiesAnimator = new AnimationProperties().setDuration(200).setAnimationEndAction((property) -> { - if (mPanelAlphaEndAction != null) { - mPanelAlphaEndAction.run(); - } - }).setCustomInterpolator( + if (mPanelAlphaEndAction != null) { + mPanelAlphaEndAction.run(); + } + }).setCustomInterpolator( mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN); private final NotificationEntryManager mEntryManager; @@ -517,19 +533,10 @@ public class NotificationPanelViewController extends PanelViewController { private final MediaDataManager mMediaDataManager; private final SysUiState mSysUiState; - private NotificationShadeDepthController mDepthController; - private int mDisplayId; + private final NotificationShadeDepthController mDepthController; + private final int mDisplayId; - /** - * Cache the resource id of the theme to avoid unnecessary work in onThemeChanged. - * - * onThemeChanged is forced when the theme might not have changed. So, to avoid unncessary - * work, check the current id with the cached id. - */ - private int mThemeResId; private KeyguardIndicationController mKeyguardIndicationController; - private int mShelfHeight; - private int mDarkIconSize; private int mHeadsUpInset; private boolean mHeadsUpPinnedMode; private boolean mAllowExpandForSmallExpansion; @@ -643,15 +650,12 @@ public class NotificationPanelViewController extends PanelViewController { private final ContentResolver mContentResolver; private float mMinFraction; - private final Executor mUiExecutor; - private final SecureSettings mSecureSettings; - - private KeyguardMediaController mKeyguardMediaController; + private final KeyguardMediaController mKeyguardMediaController; private boolean mStatusViewCentered = true; - private Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition; - private Optional<NotificationPanelUnfoldAnimationController> + private final Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition; + private final Optional<NotificationPanelUnfoldAnimationController> mNotificationPanelUnfoldAnimationController; /** The drag distance required to fully expand the split shade. */ @@ -662,9 +666,13 @@ public class NotificationPanelViewController extends PanelViewController { private final NPVCDownEventState.Buffer mLastDownEvents; - private View.AccessibilityDelegate mAccessibilityDelegate = new View.AccessibilityDelegate() { + private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = + () -> mKeyguardBottomArea.setVisibility(View.GONE); + + private final AccessibilityDelegate mAccessibilityDelegate = new AccessibilityDelegate() { @Override - public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { + public void onInitializeAccessibilityNodeInfo(View host, + AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD); info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP); @@ -672,7 +680,8 @@ public class NotificationPanelViewController extends PanelViewController { @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { - if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId() + if (action + == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId() || action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) { mStatusBarKeyguardViewManager.showBouncer(true); @@ -699,7 +708,6 @@ public class NotificationPanelViewController extends PanelViewController { @Inject public NotificationPanelViewController(NotificationPanelView view, - @Main Resources resources, @Main Handler handler, LayoutInflater layoutInflater, FeatureFlags featureFlags, @@ -749,8 +757,6 @@ public class NotificationPanelViewController extends PanelViewController { QuickAccessWalletController quickAccessWalletController, QRCodeScannerController qrCodeScannerController, RecordingController recordingController, - @Main Executor uiExecutor, - SecureSettings secureSettings, LargeScreenShadeHeaderController largeScreenShadeHeaderController, ScreenOffAnimationController screenOffAnimationController, LockscreenGestureLogger lockscreenGestureLogger, @@ -761,6 +767,7 @@ public class NotificationPanelViewController extends PanelViewController { InteractionJankMonitor interactionJankMonitor, QsFrameTranslateController qsFrameTranslateController, SysUiState sysUiState, + Provider<KeyguardBottomAreaViewController> keyguardBottomAreaViewControllerProvider, KeyguardUnlockAnimationController keyguardUnlockAnimationController, NotificationListContainer notificationListContainer, PanelEventsEmitter panelEventsEmitter, @@ -802,6 +809,7 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationsQSContainerController = notificationsQSContainerController; mNotificationListContainer = notificationListContainer; mNotificationStackSizeCalculator = notificationStackSizeCalculator; + mKeyguardBottomAreaViewControllerProvider = keyguardBottomAreaViewControllerProvider; mNotificationsQSContainerController.init(); mNotificationStackScrollLayoutController = notificationStackScrollLayoutController; mNotificationIconAreaController = notificationIconAreaController; @@ -835,8 +843,6 @@ public class NotificationPanelViewController extends PanelViewController { mUserManager = userManager; mMediaDataManager = mediaDataManager; mTapAgainViewController = tapAgainViewController; - mUiExecutor = uiExecutor; - mSecureSettings = secureSettings; mInteractionJankMonitor = interactionJankMonitor; mSysUiState = sysUiState; mPanelEventsEmitter = panelEventsEmitter; @@ -846,7 +852,6 @@ public class NotificationPanelViewController extends PanelViewController { } }); statusBarWindowStateController.addListener(this::onStatusBarWindowStateChanged); - mThemeResId = mView.getContext().getThemeResId(); mKeyguardBypassController = bypassController; mUpdateMonitor = keyguardUpdateMonitor; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; @@ -893,14 +898,14 @@ public class NotificationPanelViewController extends PanelViewController { mView.getOverlay().add(new DebugDrawable()); } - mKeyguardUnfoldTransition = unfoldComponent.map(c -> c.getKeyguardUnfoldTransition()); + mKeyguardUnfoldTransition = unfoldComponent.map( + SysUIUnfoldComponent::getKeyguardUnfoldTransition); mNotificationPanelUnfoldAnimationController = unfoldComponent.map( SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); mQsFrameTranslateController = qsFrameTranslateController; updateUserSwitcherFlags(); onFinishInflate(); - mUseCombinedQSHeaders = featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS); keyguardUnlockAnimationController.addKeyguardUnlockAnimationListener( new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() { @Override @@ -976,8 +981,8 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardStatusBarViewController = mKeyguardStatusBarViewComponentFactory.build( - mKeyguardStatusBar, - mNotificationPanelViewStateProvider) + mKeyguardStatusBar, + mNotificationPanelViewStateProvider) .getKeyguardStatusBarViewController(); mKeyguardStatusBarViewController.init(); @@ -1048,12 +1053,8 @@ public class NotificationPanelViewController extends PanelViewController { mQsPeekHeight = mResources.getDimensionPixelSize(R.dimen.qs_peek_height); mClockPositionAlgorithm.loadDimens(mResources); mQsFalsingThreshold = mResources.getDimensionPixelSize(R.dimen.qs_falsing_threshold); - mPositionMinSideMargin = mResources.getDimensionPixelSize( - R.dimen.notification_panel_min_side_margin); mIndicationBottomPadding = mResources.getDimensionPixelSize( R.dimen.keyguard_indication_bottom_padding); - mShelfHeight = mResources.getDimensionPixelSize(R.dimen.notification_shelf_height); - mDarkIconSize = mResources.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark); int statusbarHeight = SystemBarUtils.getStatusBarHeight(mView.getContext()); mHeadsUpInset = statusbarHeight + mResources.getDimensionPixelSize( R.dimen.heads_up_status_bar_padding); @@ -1118,16 +1119,12 @@ public class NotificationPanelViewController extends PanelViewController { private void setCentralSurfaces(CentralSurfaces centralSurfaces) { // TODO: this can be injected. mCentralSurfaces = centralSurfaces; - mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces); } public void updateResources() { mSplitShadeNotificationsScrimMarginBottom = mResources.getDimensionPixelSize( R.dimen.split_shade_notifications_scrim_margin_bottom); - mShelfAndLockIconOverlap = - mResources.getDimensionPixelSize(R.dimen.shelf_and_lock_icon_overlap); - final boolean newSplitShadeEnabled = LargeScreenUtils.shouldUseSplitNotificationShade(mResources); final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled; @@ -1253,8 +1250,7 @@ public class NotificationPanelViewController extends PanelViewController { int index = mView.indexOfChild(mKeyguardBottomArea); mView.removeView(mKeyguardBottomArea); KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea; - mKeyguardBottomArea = (KeyguardBottomAreaView) mLayoutInflater.inflate( - R.layout.keyguard_bottom_area, mView, false); + mKeyguardBottomArea = mKeyguardBottomAreaViewControllerProvider.get().getView(); mKeyguardBottomArea.initFrom(oldBottomArea); mView.addView(mKeyguardBottomArea, index); initBottomArea(); @@ -1292,11 +1288,11 @@ public class NotificationPanelViewController extends PanelViewController { } private void initBottomArea() { - mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces); - mKeyguardBottomArea.setFalsingManager(mFalsingManager); - mKeyguardBottomArea.initWallet(mQuickAccessWalletController); - mKeyguardBottomArea.initControls(mControlsComponent); - mKeyguardBottomArea.initQRCodeScanner(mQRCodeScannerController); + mKeyguardBottomArea.init( + mFalsingManager, + mQuickAccessWalletController, + mControlsComponent, + mQRCodeScannerController); } @VisibleForTesting @@ -1306,7 +1302,7 @@ public class NotificationPanelViewController extends PanelViewController { private void updateMaxDisplayedNotifications(boolean recompute) { if (recompute) { - mMaxAllowedKeyguardNotifications = Math.max(computeMaxKeyguardNotifications(), 1); + setMaxDisplayedNotifications(Math.max(computeMaxKeyguardNotifications(), 1)); } else { if (SPEW_LOGCAT) Log.d(TAG, "Skipping computeMaxKeyguardNotifications() by request"); } @@ -1334,7 +1330,7 @@ public class NotificationPanelViewController extends PanelViewController { private void updateGestureExclusionRect() { Rect exclusionRect = calculateGestureExclusionRect(); - mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.EMPTY_LIST + mView.setSystemGestureExclusionRects(exclusionRect.isEmpty() ? Collections.emptyList() : Collections.singletonList(exclusionRect)); } @@ -1362,14 +1358,11 @@ public class NotificationPanelViewController extends PanelViewController { mQsSizeChangeAnimator = ValueAnimator.ofInt(oldHeight, newHeight); mQsSizeChangeAnimator.setDuration(300); mQsSizeChangeAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); - mQsSizeChangeAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - requestScrollerTopPaddingUpdate(false /* animate */); - requestPanelHeightUpdate(); - int height = (int) mQsSizeChangeAnimator.getAnimatedValue(); - mQs.setHeightOverride(height); - } + mQsSizeChangeAnimator.addUpdateListener(animation -> { + requestScrollerTopPaddingUpdate(false /* animate */); + requestPanelHeightUpdate(); + int height = (int) mQsSizeChangeAnimator.getAnimatedValue(); + mQs.setHeightOverride(height); }); mQsSizeChangeAnimator.addListener(new AnimatorListenerAdapter() { @Override @@ -1710,11 +1703,16 @@ public class NotificationPanelViewController extends PanelViewController { setQsExpansion(mQsMinExpansionHeight); } + @Override + @VisibleForTesting + protected void cancelHeightAnimator() { + super.cancelHeightAnimator(); + } + public void cancelAnimation() { mView.animate().cancel(); } - /** * Animate QS closing by flinging it. * If QS is expanded, it will collapse into QQS and stop. @@ -1914,12 +1912,8 @@ public class NotificationPanelViewController extends PanelViewController { mListenForHeadsUp = mCollapsedOnDown && mHeadsUpManager.hasPinnedHeadsUp(); mAllowExpandForSmallExpansion = mExpectingSynthesizedDown; mTouchSlopExceededBeforeDown = mExpectingSynthesizedDown; - if (mExpectingSynthesizedDown) { - mLastEventSynthesizedDown = true; - } else { - // down but not synthesized motion event. - mLastEventSynthesizedDown = false; - } + // When false, down but not synthesized motion event. + mLastEventSynthesizedDown = mExpectingSynthesizedDown; mLastDownEvents.insert( mSystemClock.currentTimeMillis(), mDownX, @@ -1944,7 +1938,6 @@ public class NotificationPanelViewController extends PanelViewController { * * @param downX the x location where the touch started * @param downY the y location where the touch started - * * @return true if the panel could be collapsed because it stared on QQS */ private boolean canPanelCollapseOnQQS(float downX, float downY) { @@ -1953,7 +1946,7 @@ public class NotificationPanelViewController extends PanelViewController { } View header = mQs == null ? mKeyguardStatusBar : mQs.getHeader(); return downX >= mQsFrame.getX() && downX <= mQsFrame.getX() + mQsFrame.getWidth() - && downY <= header.getBottom(); + && downY <= header.getBottom(); } @@ -2098,7 +2091,7 @@ public class NotificationPanelViewController extends PanelViewController { return false; } return y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom() - || y <= mQs.getView().getY() + mQs.getView().getHeight(); + || y <= mQs.getView().getY() + mQs.getView().getHeight(); } private boolean isOpenQsEvent(MotionEvent event) { @@ -2277,15 +2270,11 @@ public class NotificationPanelViewController extends PanelViewController { } private void onQsExpansionStarted() { - onQsExpansionStarted(0); - } - - protected void onQsExpansionStarted(int overscrollAmount) { cancelQsAnimation(); cancelHeightAnimator(); // Reset scroll position and apply that position to the expanded height. - float height = mQsExpansionHeight - overscrollAmount; + float height = mQsExpansionHeight; setQsExpansion(height); requestPanelHeightUpdate(); mNotificationStackScrollLayoutController.checkSnoozeLeavebehind(); @@ -2297,7 +2286,8 @@ public class NotificationPanelViewController extends PanelViewController { } } - @VisibleForTesting void setQsExpanded(boolean expanded) { + @VisibleForTesting + void setQsExpanded(boolean expanded) { boolean changed = mQsExpanded != expanded; if (changed) { mQsExpanded = expanded; @@ -2322,13 +2312,6 @@ public class NotificationPanelViewController extends PanelViewController { } } - private final Runnable mAnimateKeyguardBottomAreaInvisibleEndRunnable = new Runnable() { - @Override - public void run() { - mKeyguardBottomArea.setVisibility(View.GONE); - } - }; - private void setKeyguardBottomAreaVisibility(int statusBarState, boolean goingToFullShade) { mKeyguardBottomArea.animate().cancel(); if (goingToFullShade) { @@ -2446,7 +2429,7 @@ public class NotificationPanelViewController extends PanelViewController { } setQSClippingBounds(); } - }; + } private void onNotificationScrolled(int newScrollPosition) { updateQSExpansionEnabledAmbient(); @@ -2611,7 +2594,7 @@ public class NotificationPanelViewController extends PanelViewController { boolean pulseExpanding = mPulseExpansionHandler.isExpanding(); if (mTransitioningToFullShadeProgress > 0.0f || pulseExpanding || (mQsClippingAnimation != null - && (mIsQsTranslationResetAnimator || mIsPulseExpansionResetAnimator))) { + && (mIsQsTranslationResetAnimator || mIsPulseExpansionResetAnimator))) { if (pulseExpanding || mIsPulseExpansionResetAnimator) { // qsTranslation should only be positive during pulse expansion because it's // already translating in from the top @@ -2630,8 +2613,8 @@ public class NotificationPanelViewController extends PanelViewController { mQs.setFancyClipping( mQsClipTop, mQsClipBottom, - radius, qsVisible - && !mSplitShadeEnabled); + radius, + qsVisible && !mSplitShadeEnabled); } mKeyguardStatusViewController.setClipBounds( clipStatusView ? mKeyguardStatusAreaClipBounds : null); @@ -2755,8 +2738,7 @@ public class NotificationPanelViewController extends PanelViewController { } } - - protected void requestScrollerTopPaddingUpdate(boolean animate) { + private void requestScrollerTopPaddingUpdate(boolean animate) { mNotificationStackScrollLayoutController.updateTopPadding( calculateNotificationsTopPadding(), animate); if (mKeyguardShowing && mKeyguardBypassController.getBypassEnabled()) { @@ -2904,7 +2886,7 @@ public class NotificationPanelViewController extends PanelViewController { * @param onFinishRunnable Runnable to be executed at the end of animation. * @param isClick If originated by click (different interpolator and duration.) */ - protected void flingSettings(float vel, int type, final Runnable onFinishRunnable, + private void flingSettings(float vel, int type, final Runnable onFinishRunnable, boolean isClick) { float target; switch (type) { @@ -2946,11 +2928,11 @@ public class NotificationPanelViewController extends PanelViewController { if (oppositeDirection) { animator.setDuration(350); } - animator.addUpdateListener(animation -> { - setQsExpansion((Float) animation.getAnimatedValue()); - }); + animator.addUpdateListener( + animation -> setQsExpansion((Float) animation.getAnimatedValue())); animator.addListener(new AnimatorListenerAdapter() { private boolean mIsCanceled; + @Override public void onAnimationStart(Animator animation) { notifyExpandingStarted(); @@ -3024,7 +3006,7 @@ public class NotificationPanelViewController extends PanelViewController { } @Override - protected int getMaxPanelHeight() { + public int getMaxPanelHeight() { int min = mStatusBarMinHeight; if (!(mBarState == KEYGUARD) && mNotificationStackScrollLayoutController.getNotGoneChildCount() == 0) { @@ -3048,7 +3030,7 @@ public class NotificationPanelViewController extends PanelViewController { maxHeight = calculatePanelHeightShade(); } maxHeight = Math.max(min, maxHeight); - if (maxHeight == 0 || isNaN(maxHeight)) { + if (maxHeight == 0) { Log.wtf(TAG, "maxPanelHeight is invalid. mOverExpansion: " + mOverExpansion + ", calculatePanelHeightQsExpanded: " + calculatePanelHeightQsExpanded() + ", calculatePanelHeightShade: " @@ -3128,7 +3110,7 @@ public class NotificationPanelViewController extends PanelViewController { } } - boolean isPanelExpanded() { + public boolean isPanelExpanded() { return mPanelExpanded; } @@ -3213,7 +3195,7 @@ public class NotificationPanelViewController extends PanelViewController { updateQsExpansion(); } - protected float getHeaderTranslation() { + private float getHeaderTranslation() { if (mBarState == KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) { return -mQs.getQsMinExpansionHeight(); } @@ -3244,14 +3226,7 @@ public class NotificationPanelViewController extends PanelViewController { getExpandedFraction()); float alpha = Math.min(expansionAlpha, 1 - computeQsExpansionFraction()); alpha *= mBottomAreaShadeAlpha; - mKeyguardBottomArea.setAffordanceAlpha(alpha); - mKeyguardBottomArea.setImportantForAccessibility( - alpha == 0f ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS - : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); - View ambientIndicationContainer = mCentralSurfaces.getAmbientIndicationContainer(); - if (ambientIndicationContainer != null) { - ambientIndicationContainer.setAlpha(alpha); - } + mKeyguardBottomArea.setComponentAlphas(alpha); mLockIconViewController.setAlpha(alpha); } @@ -3283,22 +3258,13 @@ public class NotificationPanelViewController extends PanelViewController { mMediaHierarchyManager.setCollapsingShadeFromQS(false); mMediaHierarchyManager.setQsExpanded(mQsExpanded); if (isFullyCollapsed()) { - DejankUtils.postAfterTraversal(new Runnable() { - @Override - public void run() { - setListening(false); - } - }); + DejankUtils.postAfterTraversal(() -> setListening(false)); // Workaround b/22639032: Make sure we invalidate something because else RenderThread // thinks we are actually drawing a frame put in reality we don't, so RT doesn't go // ahead with rendering and we jank. - mView.postOnAnimation(new Runnable() { - @Override - public void run() { - mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT); - } - }); + mView.postOnAnimation( + () -> mView.getParent().invalidateChild(mView, M_DUMMY_DIRTY_RECT)); } else { setListening(true); } @@ -3498,13 +3464,13 @@ public class NotificationPanelViewController extends PanelViewController { if (mUpdateMonitor.isFaceEnrolled() && !mUpdateMonitor.isFaceDetectionRunning() && !mUpdateMonitor.getUserCanSkipBouncer( - KeyguardUpdateMonitor.getCurrentUser())) { + KeyguardUpdateMonitor.getCurrentUser())) { mUpdateMonitor.requestFaceAuth(true); } else { mLockscreenGestureLogger.write(MetricsEvent.ACTION_LS_HINT, 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); mLockscreenGestureLogger - .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT); + .log(LockscreenUiEvent.LOCKSCREEN_LOCK_SHOW_HINT); startUnlockHintAnimation(); } if (mUpdateMonitor.isFaceEnrolled()) { @@ -3583,7 +3549,7 @@ public class NotificationPanelViewController extends PanelViewController { mNotificationStackScrollLayoutController.forceNoOverlappingRendering(closing); } - protected void updateExpandedHeight(float expandedHeight) { + private void updateExpandedHeight(float expandedHeight) { if (mTracking) { mNotificationStackScrollLayoutController .setExpandingVelocity(getCurrentExpandVelocity()); @@ -3606,12 +3572,10 @@ public class NotificationPanelViewController extends PanelViewController { } private void updateStatusBarIcons() { - boolean - showIconsWhenExpanded = + boolean showIconsWhenExpanded = (isPanelVisibleBecauseOfHeadsUp() || isFullWidth()) && getExpandedHeight() < getOpeningHeight(); - boolean noVisibleNotifications = true; - if (showIconsWhenExpanded && noVisibleNotifications && isOnKeyguard()) { + if (showIconsWhenExpanded && isOnKeyguard()) { showIconsWhenExpanded = false; } if (showIconsWhenExpanded != mShowIconsWhenExpanded) { @@ -3903,21 +3867,22 @@ public class NotificationPanelViewController extends PanelViewController { */ public void startFoldToAodAnimation(Runnable endAction) { mView.animate() - .translationX(0) - .alpha(1f) - .setDuration(ANIMATION_DURATION_FOLD_TO_AOD) - .setInterpolator(EMPHASIZED_DECELERATE) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationCancel(Animator animation) { - endAction.run(); - } - @Override - public void onAnimationEnd(Animator animation) { - endAction.run(); - } - }) - .start(); + .translationX(0) + .alpha(1f) + .setDuration(ANIMATION_DURATION_FOLD_TO_AOD) + .setInterpolator(EMPHASIZED_DECELERATE) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationCancel(Animator animation) { + endAction.run(); + } + + @Override + public void onAnimationEnd(Animator animation) { + endAction.run(); + } + }) + .start(); mKeyguardStatusViewController.animateFoldToAod(); } @@ -3931,7 +3896,6 @@ public class NotificationPanelViewController extends PanelViewController { resetTranslation(); } - /** */ public void setImportantForAccessibility(int mode) { mView.setImportantForAccessibility(mode); } @@ -3979,7 +3943,6 @@ public class NotificationPanelViewController extends PanelViewController { * {@link ShadeViewManager}. */ public void updateNotificationViews(String reason) { - mNotificationStackScrollLayoutController.updateSectionBoundaries(reason); mNotificationStackScrollLayoutController.updateFooter(); mNotificationIconAreaController.updateNotificationIcons(createVisibleEntriesList()); @@ -4266,8 +4229,7 @@ public class NotificationPanelViewController extends PanelViewController { }; @Override - protected PanelViewController.OnConfigurationChangedListener - createOnConfigurationChangedListener() { + protected OnConfigurationChangedListener createOnConfigurationChangedListener() { return new OnConfigurationChangedListener(); } @@ -4324,7 +4286,7 @@ public class NotificationPanelViewController extends PanelViewController { + isFullyExpanded() + " inQs=" + isInSettings()); } mSysUiState.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED, - isFullyExpanded() && !isInSettings()) + isFullyExpanded() && !isInSettings()) .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED, isInSettings()) .commitUpdate(mDisplayId); } @@ -4500,7 +4462,6 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onThemeChanged() { if (DEBUG_LOGCAT) Log.d(TAG, "onThemeChanged"); - mThemeResId = mView.getContext().getThemeResId(); reInflateViews(); } @@ -4650,6 +4611,7 @@ public class NotificationPanelViewController extends PanelViewController { public interface NotificationPanelViewStateProvider { /** Returns the expanded height of the panel view. */ float getPanelViewExpandedHeight(); + /** * Returns true if heads up should be visible. * @@ -4711,7 +4673,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public void onViewAttachedToWindow(View v) { mFragmentService.getFragmentHostManager(mView) - .addTagListener(QS.TAG, mFragmentListener); + .addTagListener(QS.TAG, mFragmentListener); mStatusBarStateController.addCallback(mStatusBarStateListener); mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState()); mConfigurationController.addCallback(mConfigurationListener); @@ -4728,7 +4690,7 @@ public class NotificationPanelViewController extends PanelViewController { public void onViewDetachedFromWindow(View v) { unregisterSettingsChangeListener(); mFragmentService.getFragmentHostManager(mView) - .removeTagListener(QS.TAG, mFragmentListener); + .removeTagListener(QS.TAG, mFragmentListener); mStatusBarStateController.removeCallback(mStatusBarStateListener); mConfigurationController.removeCallback(mConfigurationListener); mFalsingManager.removeTapListener(mFalsingTapListener); @@ -4746,7 +4708,7 @@ public class NotificationPanelViewController extends PanelViewController { setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth()); // Update Clock Pivot - mKeyguardStatusViewController.setPivotX(mView.getWidth() / 2); + mKeyguardStatusViewController.setPivotX(((float) mView.getWidth()) / 2f); mKeyguardStatusViewController.setPivotY( (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mKeyguardStatusViewController.getClockTextSize()); @@ -4813,7 +4775,7 @@ public class NotificationPanelViewController extends PanelViewController { private final Paint mDebugPaint = new Paint(); @Override - public void draw(@NonNull Canvas canvas) { + public void draw(@androidx.annotation.NonNull @NonNull Canvas canvas) { mDebugTextUsedYPositions.clear(); mDebugPaint.setColor(Color.RED); @@ -4887,7 +4849,7 @@ public class NotificationPanelViewController extends PanelViewController { @Override public int getOpacity() { - return 0; + return PixelFormat.UNKNOWN; } } @@ -4941,6 +4903,16 @@ public class NotificationPanelViewController extends PanelViewController { return mStatusBarViewTouchEventHandler; } + @VisibleForTesting + StatusBarStateController getStatusBarStateController() { + return mStatusBarStateController; + } + + @VisibleForTesting + boolean isHintAnimationRunning() { + return mHintAnimationRunning; + } + private void onStatusBarWindowStateChanged(@StatusBarManager.WindowVisibleState int state) { if (state != WINDOW_STATE_SHOWING && mStatusBarStateController.getState() == StatusBarState.SHADE) { @@ -4957,15 +4929,16 @@ public class NotificationPanelViewController extends PanelViewController { private final ListenerSet<Listener> mListeners = new ListenerSet<>(); @Inject - PanelEventsEmitter() {} + PanelEventsEmitter() { + } @Override - public void registerListener(@NonNull Listener listener) { + public void registerListener(@androidx.annotation.NonNull @NonNull Listener listener) { mListeners.addIfAbsent(listener); } @Override - public void unregisterListener(@NonNull Listener listener) { + public void unregisterListener(@androidx.annotation.NonNull @NonNull Listener listener) { mListeners.remove(listener); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java index 1e3a02b0606b..121d69d34678 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.shade; import static android.view.WindowInsets.Type.systemBars; @@ -52,6 +52,7 @@ import android.widget.FrameLayout; import com.android.internal.view.FloatingActionMode; import com.android.internal.widget.floatingtoolbar.FloatingToolbar; import com.android.systemui.R; +import com.android.systemui.statusbar.phone.CentralSurfaces; /** * Combined keyguard and notification panel view. Also holding backdrop and scrims. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index be5b33eb0da0..b8546df75f5f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.shade; import android.app.StatusBarManager; import android.hardware.display.AmbientDisplayConfiguration; @@ -46,6 +46,9 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.window.StatusBarWindowStateController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt index ebedbf987aa2..13a5615a8b54 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt @@ -1,4 +1,4 @@ -package com.android.systemui.statusbar.phone +package com.android.systemui.shade import android.view.View import android.view.ViewGroup @@ -6,11 +6,7 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT import android.view.WindowInsets import androidx.annotation.VisibleForTesting import androidx.constraintlayout.widget.ConstraintSet -import androidx.constraintlayout.widget.ConstraintSet.BOTTOM -import androidx.constraintlayout.widget.ConstraintSet.END -import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID -import androidx.constraintlayout.widget.ConstraintSet.START -import androidx.constraintlayout.widget.ConstraintSet.TOP +import androidx.constraintlayout.widget.ConstraintSet.* import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java index 2446cf7ba412..587e0e6dd834 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java @@ -14,7 +14,7 @@ * limitations under the License */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.shade; import android.app.Fragment; import android.content.Context; diff --git a/packages/SystemUI/src/com/android/systemui/shade/OWNERS b/packages/SystemUI/src/com/android/systemui/shade/OWNERS new file mode 100644 index 000000000000..133711ee5ab2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/shade/OWNERS @@ -0,0 +1,13 @@ +per-file *Notification* = set noparent +per-file *Notification* = file:../statusbar/notification/OWNERS + +per-file NotificationsQuickSettingsContainer.java = kozynski@google.com, asc@google.com +per-file NotificationsQSContainerController.kt = kozynski@google.com, asc@google.com + +per-file NotificationShadeWindowViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com +per-file NotificationShadeWindowView.java = pixel@google.com, cinek@google.com, juliacr@google.com + +per-file NotificationPanelUnfoldAnimationController.kt = alexflo@google.com, jeffdq@google.com, juliacr@google.com + +per-file NotificationPanelView.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com +per-file NotificationPanelViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com, justinweir@google.com
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/NoOpOverScroller.kt index 2789db874249..f4db3ab9289b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/NoOpOverScroller.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/transition/NoOpOverScroller.kt @@ -1,4 +1,4 @@ -package com.android.systemui.statusbar.phone.shade.transition +package com.android.systemui.shade.transition import javax.inject.Inject diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ScrimShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt index 1b8afb9ddc1d..afd57daca10b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ScrimShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt @@ -1,4 +1,4 @@ -package com.android.systemui.statusbar.phone.shade.transition +package com.android.systemui.shade.transition import android.content.res.Configuration import android.content.res.Resources diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeOverScroller.kt index f1cedeb21e0a..6c3a028c2380 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeOverScroller.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeOverScroller.kt @@ -1,4 +1,4 @@ -package com.android.systemui.statusbar.phone.shade.transition +package com.android.systemui.shade.transition import com.android.systemui.statusbar.phone.panelstate.PanelState diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt index 71c61597ff11..58acfb40ee44 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt @@ -1,4 +1,4 @@ -package com.android.systemui.statusbar.phone.shade.transition +package com.android.systemui.shade.transition import android.content.Context import android.content.res.Configuration @@ -6,8 +6,10 @@ import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.qs.QS +import com.android.systemui.shade.NotificationPanelViewController +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController -import com.android.systemui.statusbar.phone.NotificationPanelViewController import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager import com.android.systemui.statusbar.phone.panelstate.PanelState @@ -27,7 +29,8 @@ constructor( private val context: Context, private val splitShadeOverScrollerFactory: SplitShadeOverScroller.Factory, private val noOpOverScroller: NoOpOverScroller, - private val scrimShadeTransitionController: ScrimShadeTransitionController + private val scrimShadeTransitionController: ScrimShadeTransitionController, + private val statusBarStateController: SysuiStatusBarStateController, ) { lateinit var notificationPanelViewController: NotificationPanelViewController @@ -43,7 +46,7 @@ constructor( } private val shadeOverScroller: ShadeOverScroller get() = - if (inSplitShade && propertiesInitialized()) { + if (inSplitShade && isScreenUnlocked() && propertiesInitialized()) { splitShadeOverScroller } else { noOpOverScroller @@ -90,6 +93,7 @@ constructor( """ ShadeTransitionController: inSplitShade: $inSplitShade + isScreenUnlocked: ${isScreenUnlocked()} currentPanelState: ${currentPanelState?.panelStateToString()} lastPanelExpansionChangeEvent: $lastPanelExpansionChangeEvent qs.isInitialized: ${this::qs.isInitialized} @@ -97,4 +101,7 @@ constructor( nssl.isInitialized: ${this::notificationStackScrollLayoutController.isInitialized} """.trimIndent()) } + + private fun isScreenUnlocked() = + statusBarStateController.currentOrUpcomingState == StatusBarState.SHADE } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt index c25aab8eb80f..204dd3c07d8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScroller.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt @@ -1,4 +1,4 @@ -package com.android.systemui.statusbar.phone.shade.transition +package com.android.systemui.shade.transition import android.animation.Animator import android.animation.ValueAnimator diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 9e029095ea6b..ca147286a301 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -900,16 +900,36 @@ public class KeyguardIndicationController { mStatusBarKeyguardViewManager.showBouncerMessage(message, mInitialTextColorState); } } else { - if (!mAccessibilityManager.isEnabled() - && !mAccessibilityManager.isTouchExplorationEnabled() - && mKeyguardUpdateMonitor.isUdfpsSupported() - && mKeyguardUpdateMonitor.getUserCanSkipBouncer( - KeyguardUpdateMonitor.getCurrentUser())) { - final int stringId = mKeyguardUpdateMonitor.getIsFaceAuthenticated() - ? R.string.keyguard_face_successful_unlock_press - : R.string.keyguard_unlock_press; - showBiometricMessage(mContext.getString(stringId)); + final boolean canSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer( + KeyguardUpdateMonitor.getCurrentUser()); + if (canSkipBouncer) { + final boolean faceAuthenticated = mKeyguardUpdateMonitor.getIsFaceAuthenticated(); + final boolean udfpsSupported = mKeyguardUpdateMonitor.isUdfpsSupported(); + final boolean a11yEnabled = mAccessibilityManager.isEnabled() + || mAccessibilityManager.isTouchExplorationEnabled(); + if (udfpsSupported && faceAuthenticated) { // co-ex + if (a11yEnabled) { + showBiometricMessage(mContext.getString( + R.string.keyguard_face_successful_unlock_swipe)); + } else { + showBiometricMessage(mContext.getString( + R.string.keyguard_face_successful_unlock_press)); + } + } else if (faceAuthenticated) { // face-only + showBiometricMessage(mContext.getString( + R.string.keyguard_face_successful_unlock_swipe)); + } else if (udfpsSupported) { // udfps-only + if (a11yEnabled) { + showBiometricMessage(mContext.getString(R.string.keyguard_unlock)); + } else { + showBiometricMessage(mContext.getString( + R.string.keyguard_unlock_press)); + } + } else { // no security or unlocked by a trust agent + showBiometricMessage(mContext.getString(R.string.keyguard_unlock)); + } } else { + // suggest swiping up for the primary authentication bouncer showBiometricMessage(mContext.getString(R.string.keyguard_unlock)); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt index 01eb4446ea8e..886ad684649f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt @@ -6,7 +6,7 @@ import android.util.MathUtils import com.android.systemui.R import com.android.systemui.dump.DumpManager import com.android.systemui.media.MediaHierarchyManager -import com.android.systemui.statusbar.phone.NotificationPanelViewController +import com.android.systemui.shade.NotificationPanelViewController import com.android.systemui.statusbar.policy.ConfigurationController import dagger.assisted.Assisted import dagger.assisted.AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 9d80d5fbc2ca..74d8f1beaec1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -29,6 +29,7 @@ import com.android.systemui.plugins.ActivityStarter.OnDismissAction import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QS import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.shade.NotificationPanelViewController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView @@ -37,7 +38,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.LSShadeTransitionLogger -import com.android.systemui.statusbar.phone.NotificationPanelViewController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.LargeScreenUtils import java.io.PrintWriter diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java index 5cdd01f0ee0b..78b3b0a65351 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java @@ -286,10 +286,6 @@ public class NotificationRemoteInputManager implements Dumpable { ServiceManager.getService(Context.STATUS_BAR_SERVICE)); mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mRebuilder = rebuilder; - if (!mNotifPipelineFlags.isNewPipelineEnabled()) { - mRemoteInputListener = createLegacyRemoteInputLifetimeExtender(mainHandler, - notificationEntryManager, smartReplyController); - } mKeyguardManager = context.getSystemService(KeyguardManager.class); mStatusBarStateController = statusBarStateController; mRemoteInputUriController = remoteInputUriController; @@ -313,34 +309,19 @@ public class NotificationRemoteInputManager implements Dumpable { int reason) { // We're removing the notification, the smart controller can forget about it. mSmartReplyController.stopSending(entry); - - if (removedByUser && entry != null) { - onPerformRemoveNotification(entry, entry.getKey()); - } } }); } /** Add a listener for various remote input events. Works with NEW pipeline only. */ public void setRemoteInputListener(@NonNull RemoteInputListener remoteInputListener) { - if (mNotifPipelineFlags.isNewPipelineEnabled()) { - if (mRemoteInputListener != null) { - throw new IllegalStateException("mRemoteInputListener is already set"); - } - mRemoteInputListener = remoteInputListener; - if (mRemoteInputController != null) { - mRemoteInputListener.setRemoteInputController(mRemoteInputController); - } + if (mRemoteInputListener != null) { + throw new IllegalStateException("mRemoteInputListener is already set"); + } + mRemoteInputListener = remoteInputListener; + if (mRemoteInputController != null) { + mRemoteInputListener.setRemoteInputController(mRemoteInputController); } - } - - @NonNull - @VisibleForTesting - protected LegacyRemoteInputLifetimeExtender createLegacyRemoteInputLifetimeExtender( - Handler mainHandler, - NotificationEntryManager notificationEntryManager, - SmartReplyController smartReplyController) { - return new LegacyRemoteInputLifetimeExtender(); } /** Initializes this component with the provided dependencies. */ @@ -381,12 +362,6 @@ public class NotificationRemoteInputManager implements Dumpable { } } }); - if (!mNotifPipelineFlags.isNewPipelineEnabled()) { - mSmartReplyController.setCallback((entry, reply) -> { - StatusBarNotification newSbn = mRebuilder.rebuildForSendingSmartReply(entry, reply); - mEntryManager.updateNotification(newSbn, null /* ranking */); - }); - } } public void addControllerCallback(RemoteInputController.Callback callback) { @@ -588,19 +563,6 @@ public class NotificationRemoteInputManager implements Dumpable { return v.findViewWithTag(RemoteInputView.VIEW_TAG); } - public ArrayList<NotificationLifetimeExtender> getLifetimeExtenders() { - // OLD pipeline code ONLY; can assume implementation - return ((LegacyRemoteInputLifetimeExtender) mRemoteInputListener).mLifetimeExtenders; - } - - @VisibleForTesting - void onPerformRemoveNotification(NotificationEntry entry, final String key) { - // OLD pipeline code ONLY; can assume implementation - ((LegacyRemoteInputLifetimeExtender) mRemoteInputListener) - .mKeysKeptForRemoteInputHistory.remove(key); - cleanUpRemoteInputForUserRemoval(entry); - } - /** * Disable remote input on the entry and remove the remote input view. * This should be called when a user dismisses a notification that won't be lifetime extended. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index a1463401460f..221428747558 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -398,7 +398,14 @@ public class NotificationShelf extends ActivatableNotificationView implements if (child instanceof ActivatableNotificationView) { ActivatableNotificationView anv = (ActivatableNotificationView) child; - updateCornerRoundnessOnScroll(anv, viewStart, shelfStart); + // Because we show whole notifications on the lockscreen, the bottom notification is + // always "just about to enter the shelf" by normal scrolling rules. This is fine + // if the shelf is visible, but if the shelf is hidden, it causes incorrect curling. + // notificationClipEnd handles the discrepancy between a visible and hidden shelf, + // so we use that when on the keyguard (and while animating away) to reduce curling. + final float keyguardSafeShelfStart = + mAmbientState.isOnKeyguard() ? notificationClipEnd : shelfStart; + updateCornerRoundnessOnScroll(anv, viewStart, keyguardSafeShelfStart); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index 587c23b39dd3..9e77dbc08c7a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -68,6 +68,7 @@ import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconControllerImpl; +import com.android.systemui.statusbar.phone.StatusBarIconList; import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallFlags; @@ -256,6 +257,16 @@ public interface CentralSurfacesDependenciesModule { */ @Provides @SysUISingleton + static StatusBarIconList provideStatusBarIconList(Context context) { + return new StatusBarIconList( + context.getResources().getStringArray( + com.android.internal.R.array.config_statusBarIcons)); + } + + /** + */ + @Provides + @SysUISingleton static OngoingCallController provideOngoingCallController( Context context, CommonNotifCollection notifCollection, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index 5aeab84b677c..d24896148095 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -31,7 +31,6 @@ import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow @@ -129,11 +128,9 @@ class AnimatedImageNotificationManager @Inject constructor( */ @SysUISingleton class ConversationNotificationManager @Inject constructor( - private val bindEventManager: BindEventManager, - private val notificationGroupManager: NotificationGroupManagerLegacy, + bindEventManager: BindEventManager, private val context: Context, private val notifCollection: CommonNotifCollection, - private val featureFlags: NotifPipelineFlags, @Main private val mainHandler: Handler ) { // Need this state to be thread safe, since it's accessed from the ui thread @@ -172,12 +169,10 @@ class ConversationNotificationManager @Inject constructor( layout.setIsImportantConversation(important, false) } } - if (changed && !featureFlags.isNewPipelineEnabled()) { - notificationGroupManager.updateIsolation(entry) - } } } } + fun onEntryViewBound(entry: NotificationEntry) { if (!entry.ranking.isConversation) { return diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index c86bf93628dd..852d5f7a16e6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -41,6 +41,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.NotificationVisibility; import com.android.systemui.Dumpable; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.NotificationLifetimeExtender; import com.android.systemui.statusbar.NotificationListener; @@ -73,6 +74,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.Executor; import dagger.Lazy; @@ -113,6 +115,7 @@ public class NotificationEntryManager implements private final IStatusBarService mStatusBarService; private final NotifLiveDataStoreImpl mNotifLiveDataStore; private final DumpManager mDumpManager; + private final Executor mBgExecutor; private final Set<NotificationEntry> mAllNotifications = new ArraySet<>(); private final Set<NotificationEntry> mReadOnlyAllNotifications = @@ -159,7 +162,8 @@ public class NotificationEntryManager implements LeakDetector leakDetector, IStatusBarService statusBarService, NotifLiveDataStoreImpl notifLiveDataStore, - DumpManager dumpManager + DumpManager dumpManager, + @Background Executor bgExecutor ) { mLogger = logger; mGroupManager = groupManager; @@ -170,6 +174,7 @@ public class NotificationEntryManager implements mStatusBarService = statusBarService; mNotifLiveDataStore = notifLiveDataStore; mDumpManager = dumpManager; + mBgExecutor = bgExecutor; } /** Once called, the NEM will start processing notification events from system server. */ @@ -566,17 +571,19 @@ public class NotificationEntryManager implements private void sendNotificationRemovalToServer( StatusBarNotification notification, DismissedByUserStats dismissedByUserStats) { - try { - mStatusBarService.onNotificationClear( - notification.getPackageName(), - notification.getUser().getIdentifier(), - notification.getKey(), - dismissedByUserStats.dismissalSurface, - dismissedByUserStats.dismissalSentiment, - dismissedByUserStats.notificationVisibility); - } catch (RemoteException ex) { - // system process is dead if we're here. - } + mBgExecutor.execute(() -> { + try { + mStatusBarService.onNotificationClear( + notification.getPackageName(), + notification.getUser().getIdentifier(), + notification.getKey(), + dismissedByUserStats.dismissalSurface, + dismissedByUserStats.dismissalSentiment, + dismissedByUserStats.notificationVisibility); + } catch (RemoteException ex) { + // system process is dead if we're here. + } + }); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt index b764c271de45..31b21c9b5321 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt @@ -4,10 +4,10 @@ import android.view.ViewGroup import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.animation.LaunchAnimator +import com.android.systemui.shade.NotificationShadeWindowViewController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.phone.HeadsUpManagerPhone -import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent import com.android.systemui.statusbar.policy.HeadsUpUtil import javax.inject.Inject 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 d99b5f9df12a..351a4bea2947 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 @@ -70,6 +70,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.LogBufferEulogizer; @@ -112,6 +113,7 @@ import java.util.Map; import java.util.Objects; import java.util.Queue; import java.util.Set; +import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import javax.inject.Inject; @@ -146,6 +148,7 @@ public class NotifCollection implements Dumpable { private final NotifPipelineFlags mNotifPipelineFlags; private final NotifCollectionLogger mLogger; private final Handler mMainHandler; + private final Executor mBgExecutor; private final LogBufferEulogizer mEulogizer; private final DumpManager mDumpManager; @@ -174,6 +177,7 @@ public class NotifCollection implements Dumpable { NotifPipelineFlags notifPipelineFlags, NotifCollectionLogger logger, @Main Handler mainHandler, + @Background Executor bgExecutor, LogBufferEulogizer logBufferEulogizer, DumpManager dumpManager) { mStatusBarService = statusBarService; @@ -181,6 +185,7 @@ public class NotifCollection implements Dumpable { mNotifPipelineFlags = notifPipelineFlags; mLogger = logger; mMainHandler = mainHandler; + mBgExecutor = bgExecutor; mEulogizer = logBufferEulogizer; mDumpManager = dumpManager; } @@ -294,18 +299,20 @@ public class NotifCollection implements Dumpable { entriesToLocallyDismiss.add(entry); if (!isCanceled(entry)) { // send message to system server if this notification hasn't already been cancelled - try { - mStatusBarService.onNotificationClear( - entry.getSbn().getPackageName(), - entry.getSbn().getUser().getIdentifier(), - entry.getSbn().getKey(), - stats.dismissalSurface, - stats.dismissalSentiment, - stats.notificationVisibility); - } catch (RemoteException e) { - // system process is dead if we're here. - mLogger.logRemoteExceptionOnNotificationClear(entry, e); - } + mBgExecutor.execute(() -> { + try { + mStatusBarService.onNotificationClear( + entry.getSbn().getPackageName(), + entry.getSbn().getUser().getIdentifier(), + entry.getSbn().getKey(), + stats.dismissalSurface, + stats.dismissalSentiment, + stats.notificationVisibility); + } catch (RemoteException e) { + // system process is dead if we're here. + mLogger.logRemoteExceptionOnNotificationClear(entry, e); + } + }); } } @@ -578,12 +585,10 @@ public class NotifCollection implements Dumpable { // TODO: (b/145659174) update the sbn's overrideGroupKey in // NotificationEntry.setRanking instead of here once we fully migrate to the // NewNotifPipeline - if (mNotifPipelineFlags.isNewPipelineEnabled()) { - final String newOverrideGroupKey = ranking.getOverrideGroupKey(); - if (!Objects.equals(entry.getSbn().getOverrideGroupKey(), - newOverrideGroupKey)) { - entry.getSbn().setOverrideGroupKey(newOverrideGroupKey); - } + final String newOverrideGroupKey = ranking.getOverrideGroupKey(); + if (!Objects.equals(entry.getSbn().getOverrideGroupKey(), + newOverrideGroupKey)) { + entry.getSbn().setOverrideGroupKey(newOverrideGroupKey); } } else { if (currentEntriesWithoutRankings == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java index 6a3799b38dc5..d1aa01bb2125 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java @@ -75,8 +75,8 @@ public class NotifInflaterImpl implements NotifInflater { } @Override - public void abortInflation(NotificationEntry entry) { - entry.abortTask(); + public boolean abortInflation(NotificationEntry entry) { + return entry.abortTask(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt index e3ee8139d71c..f662a040fae6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.notification.collection import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener @@ -76,7 +75,6 @@ import javax.inject.Inject */ @SysUISingleton class NotifPipeline @Inject constructor( - notifPipelineFlags: NotifPipelineFlags, private val mNotifCollection: NotifCollection, private val mShadeListBuilder: ShadeListBuilder, private val mRenderStageManager: RenderStageManager @@ -107,8 +105,6 @@ class NotifPipeline @Inject constructor( return mNotifCollection.getEntry(key) } - val isNewPipelineEnabled: Boolean = notifPipelineFlags.isNewPipelineEnabled() - /** * Registers a lifetime extender. Lifetime extenders can cause notifications that have been * dismissed or retracted by system server to be temporarily retained in the collection. @@ -253,4 +249,4 @@ class NotifPipeline @Inject constructor( fun getInternalNotifUpdater(name: String?): InternalNotifUpdater { return mNotifCollection.getInternalNotifUpdater(name) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 0a16fb65b1ac..b6392f705c49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -476,11 +476,13 @@ public final class NotificationEntry extends ListEntry { /** * Abort all existing inflation tasks */ - public void abortTask() { + public boolean abortTask() { if (mRunningTask != null) { mRunningTask.abort(); mRunningTask = null; + return true; } + return false; } public void setInflationTask(InflationTask abortableTask) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java index 93761f580dd4..075a0dc7555e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java @@ -314,60 +314,62 @@ public class ShadeListBuilder implements Dumpable { } }; - private void onPreRenderInvalidated(Invalidator invalidator) { + private void onPreRenderInvalidated(Invalidator invalidator, @Nullable String reason) { Assert.isMainThread(); - mLogger.logPreRenderInvalidated(invalidator.getName(), mPipelineState.getState()); + mLogger.logPreRenderInvalidated(invalidator, mPipelineState.getState(), reason); rebuildListIfBefore(STATE_FINALIZING); } - private void onPreGroupFilterInvalidated(NotifFilter filter) { + private void onPreGroupFilterInvalidated(NotifFilter filter, @Nullable String reason) { Assert.isMainThread(); - mLogger.logPreGroupFilterInvalidated(filter.getName(), mPipelineState.getState()); + mLogger.logPreGroupFilterInvalidated(filter, mPipelineState.getState(), reason); rebuildListIfBefore(STATE_PRE_GROUP_FILTERING); } - private void onReorderingAllowedInvalidated(NotifStabilityManager stabilityManager) { + private void onReorderingAllowedInvalidated(NotifStabilityManager stabilityManager, + @Nullable String reason) { Assert.isMainThread(); mLogger.logReorderingAllowedInvalidated( - stabilityManager.getName(), - mPipelineState.getState()); + stabilityManager, + mPipelineState.getState(), + reason); rebuildListIfBefore(STATE_GROUPING); } - private void onPromoterInvalidated(NotifPromoter promoter) { + private void onPromoterInvalidated(NotifPromoter promoter, @Nullable String reason) { Assert.isMainThread(); - mLogger.logPromoterInvalidated(promoter.getName(), mPipelineState.getState()); + mLogger.logPromoterInvalidated(promoter, mPipelineState.getState(), reason); rebuildListIfBefore(STATE_TRANSFORMING); } - private void onNotifSectionInvalidated(NotifSectioner section) { + private void onNotifSectionInvalidated(NotifSectioner section, @Nullable String reason) { Assert.isMainThread(); - mLogger.logNotifSectionInvalidated(section.getName(), mPipelineState.getState()); + mLogger.logNotifSectionInvalidated(section, mPipelineState.getState(), reason); rebuildListIfBefore(STATE_SORTING); } - private void onFinalizeFilterInvalidated(NotifFilter filter) { + private void onFinalizeFilterInvalidated(NotifFilter filter, @Nullable String reason) { Assert.isMainThread(); - mLogger.logFinalizeFilterInvalidated(filter.getName(), mPipelineState.getState()); + mLogger.logFinalizeFilterInvalidated(filter, mPipelineState.getState(), reason); rebuildListIfBefore(STATE_FINALIZE_FILTERING); } - private void onNotifComparatorInvalidated(NotifComparator comparator) { + private void onNotifComparatorInvalidated(NotifComparator comparator, @Nullable String reason) { Assert.isMainThread(); - mLogger.logNotifComparatorInvalidated(comparator.getName(), mPipelineState.getState()); + mLogger.logNotifComparatorInvalidated(comparator, mPipelineState.getState(), reason); rebuildListIfBefore(STATE_SORTING); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java index b923fdfe6be8..3627b4084ec7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java @@ -127,14 +127,6 @@ public class BubbleCoordinator implements Coordinator { DismissedByUserStats dismissedByUserStats, int reason ) { - if (!mNotifPipeline.isNewPipelineEnabled()) { - // The `entry` will be from whichever pipeline is active, so if the old pipeline is - // running, make sure that we use the new pipeline's entry (if it still exists). - NotificationEntry newPipelineEntry = mNotifPipeline.getEntry(entry.getKey()); - if (newPipelineEntry != null) { - entry = newPipelineEntry; - } - } if (isInterceptingDismissal(entry)) { mInterceptedDismissalEntries.remove(entry.getKey()); mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry, @@ -150,7 +142,7 @@ public class BubbleCoordinator implements Coordinator { @Override public void invalidateNotifications(String reason) { - mNotifFilter.invalidateList(); + mNotifFilter.invalidateList(reason); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt index df54ccd85e73..240540515705 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt @@ -30,12 +30,12 @@ class DebugModeCoordinator @Inject constructor( ) : Coordinator { override fun attach(pipeline: NotifPipeline) { - pipeline.addPreGroupFilter(preGroupFilter) - debugModeFilterProvider.registerInvalidationListener(preGroupFilter::invalidateList) + pipeline.addPreGroupFilter(filter) + debugModeFilterProvider.registerInvalidationListener { filter.invalidateList(null) } } - private val preGroupFilter = object : NotifFilter("DebugModeCoordinator") { + private val filter = object : NotifFilter("DebugModeFilter") { override fun shouldFilterOut(entry: NotificationEntry, now: Long) = debugModeFilterProvider.shouldFilterOut(entry) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java index e8652493da6b..058042c4bccd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java @@ -90,7 +90,7 @@ public class DeviceProvisionedCoordinator implements Coordinator { new DeviceProvisionedController.DeviceProvisionedListener() { @Override public void onDeviceProvisionedChanged() { - mNotifFilter.invalidateList(); + mNotifFilter.invalidateList("onDeviceProvisionedChanged"); } }; } 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 a61db4107c94..8278b549a7a0 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 @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.notification.collection.render.NodeControl import com.android.systemui.statusbar.notification.dagger.IncomingHeader import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider +import com.android.systemui.statusbar.notification.logKey import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP import com.android.systemui.statusbar.policy.HeadsUpManager import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener @@ -278,8 +279,8 @@ class HeadsUpCoordinator @Inject constructor( .firstOrNull() ?.let { posted -> posted.entry.takeIf { entry -> - locationLookupByKey(entry.key) == GroupLocation.Isolated - && entry.sbn.notification.groupAlertBehavior == GROUP_ALERT_SUMMARY + locationLookupByKey(entry.key) == GroupLocation.Isolated && + entry.sbn.notification.groupAlertBehavior == GROUP_ALERT_SUMMARY } } @@ -512,7 +513,7 @@ class HeadsUpCoordinator @Inject constructor( private val mOnHeadsUpChangedListener = object : OnHeadsUpChangedListener { override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) { if (!isHeadsUp) { - mNotifPromoter.invalidateList() + mNotifPromoter.invalidateList("headsUpEnded: ${entry.logKey}") mHeadsUpViewBinder.unbindHeadsUpView(entry) endNotifLifetimeExtensionIfExtended(entry) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java index 7b5cf8511900..e4e2bc16b77e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinator.java @@ -41,14 +41,11 @@ import javax.inject.Inject; @CoordinatorScope public class HideNotifsForOtherUsersCoordinator implements Coordinator { private final NotificationLockscreenUserManager mLockscreenUserManager; - private final SharedCoordinatorLogger mLogger; @Inject public HideNotifsForOtherUsersCoordinator( - NotificationLockscreenUserManager lockscreenUserManager, - SharedCoordinatorLogger logger) { + NotificationLockscreenUserManager lockscreenUserManager) { mLockscreenUserManager = lockscreenUserManager; - mLogger = logger; } @Override @@ -70,23 +67,19 @@ public class HideNotifsForOtherUsersCoordinator implements Coordinator { // changes @Override public void onCurrentProfilesChanged(SparseArray<UserInfo> currentProfiles) { - mLogger.logUserOrProfileChanged( - mLockscreenUserManager.getCurrentUserId(), - profileIdsToStr(currentProfiles)); - mFilter.invalidateList(); + StringBuilder sb = new StringBuilder("onCurrentProfilesChanged:"); + sb.append(" user=").append(mLockscreenUserManager.getCurrentUserId()); + sb.append(" profiles="); + sb.append("{"); + for (int i = 0; i < currentProfiles.size(); i++) { + if (i != 0) { + sb.append(","); + } + sb.append(currentProfiles.keyAt(i)); + } + sb.append("}"); + mFilter.invalidateList(sb.toString()); } }; - private String profileIdsToStr(SparseArray<UserInfo> currentProfiles) { - StringBuilder sb = new StringBuilder(); - sb.append("{"); - for (int i = 0; i < currentProfiles.size(); i++) { - sb.append(currentProfiles.keyAt(i)); - if (i < currentProfiles.size() - 1) { - sb.append(","); - } - } - sb.append("}"); - return sb.toString(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java index ef63be0633bf..e3d71c8b29d9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java @@ -38,18 +38,15 @@ public class KeyguardCoordinator implements Coordinator { private static final String TAG = "KeyguardCoordinator"; private final KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider; private final SectionHeaderVisibilityProvider mSectionHeaderVisibilityProvider; - private final SharedCoordinatorLogger mLogger; private final StatusBarStateController mStatusBarStateController; @Inject public KeyguardCoordinator( KeyguardNotificationVisibilityProvider keyguardNotificationVisibilityProvider, SectionHeaderVisibilityProvider sectionHeaderVisibilityProvider, - SharedCoordinatorLogger logger, StatusBarStateController statusBarStateController) { mKeyguardNotificationVisibilityProvider = keyguardNotificationVisibilityProvider; mSectionHeaderVisibilityProvider = sectionHeaderVisibilityProvider; - mLogger = logger; mStatusBarStateController = statusBarStateController; } @@ -78,9 +75,8 @@ public class KeyguardCoordinator implements Coordinator { } private void invalidateListFromFilter(String reason) { - mLogger.logKeyguardCoordinatorInvalidated(reason); updateSectionHeadersVisibility(); - mNotifFilter.invalidateList(); + mNotifFilter.invalidateList(reason); } private void updateSectionHeadersVisibility() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt index 359e2028d9f3..891e25ef6c25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt @@ -72,9 +72,7 @@ class NotifCoordinatorsImpl @Inject constructor( // pipeline, such as this DataStoreCoordinator which cannot be removed, as it's a critical // glue between the pipeline and parts of SystemUI which depend on pipeline output via the // NotifLiveDataStore. - if (notifPipelineFlags.isNewPipelineEnabled()) { - mCoordinators.add(dataStoreCoordinator) - } + mCoordinators.add(dataStoreCoordinator) // Attach normal coordinators. mCoordinators.add(hideLocallyDismissedNotifsCoordinator) @@ -97,18 +95,14 @@ class NotifCoordinatorsImpl @Inject constructor( if (notifPipelineFlags.isSmartspaceDedupingEnabled()) { mCoordinators.add(smartspaceDedupingCoordinator) } - if (notifPipelineFlags.isNewPipelineEnabled()) { - mCoordinators.add(headsUpCoordinator) - mCoordinators.add(gutsCoordinator) - mCoordinators.add(preparationCoordinator) - mCoordinators.add(remoteInputCoordinator) - } + mCoordinators.add(headsUpCoordinator) + mCoordinators.add(gutsCoordinator) + mCoordinators.add(preparationCoordinator) + mCoordinators.add(remoteInputCoordinator) // Manually add Ordered Sections // HeadsUp > FGS > People > Alerting > Silent > Minimized > Unknown/Default - if (notifPipelineFlags.isNewPipelineEnabled()) { - mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp - } + mOrderedSections.add(headsUpCoordinator.sectioner) mOrderedSections.add(appOpsCoordinator.sectioner) // ForegroundService mOrderedSections.add(conversationCoordinator.sectioner) // People mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java index 023c4ef2b8b7..ef1e57b4cd3b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinator.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; +import static com.android.systemui.statusbar.notification.NotificationUtils.logKey; import static com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED; import static java.util.Objects.requireNonNull; @@ -147,7 +148,8 @@ public class PreparationCoordinator implements Coordinator { @Override public void attach(NotifPipeline pipeline) { mNotifErrorManager.addInflationErrorListener(mInflationErrorListener); - mAdjustmentProvider.addDirtyListener(mNotifInflatingFilter::invalidateList); + mAdjustmentProvider.addDirtyListener( + () -> mNotifInflatingFilter.invalidateList("adjustmentProviderChanged")); pipeline.addCollectionListener(mNotifCollectionListener); // Inflate after grouping/sorting since that affects what views to inflate. @@ -245,12 +247,13 @@ public class PreparationCoordinator implements Coordinator { } catch (RemoteException ex) { // System server is dead, nothing to do about that } - mNotifInflationErrorFilter.invalidateList(); + mNotifInflationErrorFilter.invalidateList("onNotifInflationError for " + logKey(entry)); } @Override public void onNotifInflationErrorCleared(NotificationEntry entry) { - mNotifInflationErrorFilter.invalidateList(); + mNotifInflationErrorFilter.invalidateList( + "onNotifInflationErrorCleared for " + logKey(entry)); } }; @@ -360,9 +363,11 @@ public class PreparationCoordinator implements Coordinator { } private void abortInflation(NotificationEntry entry, String reason) { - mLogger.logInflationAborted(entry, reason); - mNotifInflater.abortInflation(entry); - mInflatingNotifs.remove(entry); + final boolean taskAborted = mNotifInflater.abortInflation(entry); + final boolean wasInflating = mInflatingNotifs.remove(entry); + if (taskAborted || wasInflating) { + mLogger.logInflationAborted(entry, reason); + } } private void onInflationFinished(NotificationEntry entry, NotifViewController controller) { @@ -371,7 +376,7 @@ public class PreparationCoordinator implements Coordinator { mViewBarn.registerViewForEntry(entry, controller); mInflationStates.put(entry, STATE_INFLATED); mBindEventManager.notifyViewBound(entry); - mNotifInflatingFilter.invalidateList(); + mNotifInflatingFilter.invalidateList("onInflationFinished for " + logKey(entry)); } private void freeNotifViews(NotificationEntry entry) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java index 5ac481341d43..67a8a63ad7da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java @@ -199,7 +199,7 @@ public class RankingCoordinator implements Coordinator { new StatusBarStateController.StateListener() { @Override public void onDozingChanged(boolean isDozing) { - mDndVisualEffectsFilter.invalidateList(); + mDndVisualEffectsFilter.invalidateList("onDozingChanged to " + isDozing); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt index 3f8a39f62dfb..aeeeb4fb054d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt @@ -64,7 +64,7 @@ private class SensitiveContentCoordinatorImpl @Inject constructor( pipeline.addPreRenderInvalidator(this) } - override fun onDynamicPrivacyChanged(): Unit = invalidateList() + override fun onDynamicPrivacyChanged(): Unit = invalidateList("onDynamicPrivacyChanged") override fun onBeforeRenderList(entries: List<ListEntry>) { if (keyguardStateController.isKeyguardGoingAway() || diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SharedCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SharedCoordinatorLogger.kt deleted file mode 100644 index 25bc641f0a84..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SharedCoordinatorLogger.kt +++ /dev/null @@ -1,46 +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.systemui.statusbar.notification.collection.coordinator - -import com.android.systemui.log.LogBuffer -import com.android.systemui.log.LogLevel -import com.android.systemui.log.dagger.NotificationLog -import javax.inject.Inject - -/** - * Shared logging class for coordinators that don't log enough to merit their own logger. - */ -class SharedCoordinatorLogger @Inject constructor( - @NotificationLog private val buffer: LogBuffer -) { - fun logUserOrProfileChanged(userId: Int, profiles: String) { - buffer.log("NotCurrentUserFilter", LogLevel.INFO, { - int1 = userId - str1 = profiles - }, { - "Current user or profiles changed. Current user is $int1; profiles are $str1" - }) - } - - fun logKeyguardCoordinatorInvalidated(reason: String) { - buffer.log("KeyguardCoordinator", LogLevel.DEBUG, { - str1 = reason - }, { - "KeyguardCoordinator invalidated: $str1" - }) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt index 48f00ac4bf55..9d0f974ea68f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt @@ -30,6 +30,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener +import com.android.systemui.statusbar.notification.logKey import com.android.systemui.util.concurrency.DelayableExecutor import com.android.systemui.util.time.SystemClock import java.util.concurrent.TimeUnit.SECONDS @@ -65,13 +66,6 @@ class SmartspaceDedupingCoordinator @Inject constructor( statusBarStateController.addCallback(statusBarStateListener) smartspaceController.addListener(this::onNewSmartspaceTargets) - if (!pipeline.isNewPipelineEnabled) { - // TODO (b/173126564): Remove this once the old pipeline is no longer necessary - notificationLockscreenUserManager.addKeyguardNotificationSuppressor { entry -> - isDupedWithSmartspaceContent(entry) - } - } - recordStatusBarState(statusBarStateController.state) } @@ -137,7 +131,7 @@ class SmartspaceDedupingCoordinator @Inject constructor( } if (changed) { - filter.invalidateList() + filter.invalidateList("onNewSmartspaceTargets") notificationEntryManager.updateNotifications("Smartspace targets changed") } @@ -174,7 +168,7 @@ class SmartspaceDedupingCoordinator @Inject constructor( target.cancelTimeoutRunnable = executor.executeDelayed({ target.cancelTimeoutRunnable = null target.shouldFilter = true - filter.invalidateList() + filter.invalidateList("updateAlertException: ${entry.logKey}") notificationEntryManager.updateNotifications("deduping timeout expired") }, alertExceptionExpires - now) } @@ -191,7 +185,7 @@ class SmartspaceDedupingCoordinator @Inject constructor( isOnLockscreen = newState == StatusBarState.KEYGUARD if (isOnLockscreen != wasOnLockscreen) { - filter.invalidateList() + filter.invalidateList("recordStatusBarState: " + StatusBarState.toString(newState)) // No need to call notificationEntryManager.updateNotifications; something else already // does it for us when the keyguard state changes } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt index bae16fe835f1..657c394d4e30 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt @@ -70,11 +70,9 @@ class ViewConfigCoordinator @Inject internal constructor( override fun attach(pipeline: NotifPipeline) { mPipeline = pipeline - if (pipeline.isNewPipelineEnabled) { - mLockscreenUserManager.addUserChangedListener(mUserChangedListener) - mConfigurationController.addCallback(this) - mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback) - } + mLockscreenUserManager.addUserChangedListener(mUserChangedListener) + mConfigurationController.addCallback(this) + mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback) } override fun onDensityOrFontScaleChanged() { @@ -147,4 +145,4 @@ class ViewConfigCoordinator @Inject internal constructor( private const val TAG = "ViewConfigCoordinator" private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.DEBUG) } -}
\ No newline at end of file +} 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 b9d23b4a344a..d3bc257d8a54 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 @@ -28,13 +28,13 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotifPanelEvents; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; 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; @@ -192,11 +192,16 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, + " reorderingAllowed " + wasReorderingAllowed + "->" + mReorderingAllowed + " when setting " + field + "=" + value); } - if ((mPipelineRunAllowed && mIsSuppressingPipelineRun) - || (mReorderingAllowed && (mIsSuppressingGroupChange - || isSuppressingSectionChange() - || mIsSuppressingEntryReorder))) { - mNotifStabilityManager.invalidateList(); + if (mPipelineRunAllowed && mIsSuppressingPipelineRun) { + mNotifStabilityManager.invalidateList("pipeline run suppression ended"); + } else if (mReorderingAllowed && (mIsSuppressingGroupChange + || isSuppressingSectionChange() + || mIsSuppressingEntryReorder)) { + String reason = "reorder suppression ended for" + + " group=" + mIsSuppressingGroupChange + + " section=" + isSuppressingSectionChange() + + " sort=" + mIsSuppressingEntryReorder; + mNotifStabilityManager.invalidateList(reason); } mVisualStabilityProvider.setReorderingAllowed(mReorderingAllowed); } @@ -241,7 +246,7 @@ public class VisualStabilityCoordinator implements Coordinator, Dumpable, now + ALLOW_SECTION_CHANGE_TIMEOUT)); if (!wasSectionChangeAllowed) { - mNotifStabilityManager.invalidateList(); + mNotifStabilityManager.invalidateList("temporarilyAllowSectionChanges"); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt index 567ec85cf6c7..08e21e8f668e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt @@ -42,9 +42,9 @@ interface NotifInflater { /** * Request to stop the inflation of an entry. For example, called when a notification is - * removed and no longer needs to be inflated. + * removed and no longer needs to be inflated. Returns whether anything may have been aborted. */ - fun abortInflation(entry: NotificationEntry) + fun abortInflation(entry: NotificationEntry): Boolean /** * Called to let the system remove the content views from the notification row. @@ -62,4 +62,4 @@ interface NotifInflater { * A class holding parameters used when inflating the notification row */ class Params(val isLowPriority: Boolean, val reason: String) -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java index 7dd3672a6e30..a7719d3d82a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnUserInteractionCallbackImpl.java @@ -23,6 +23,7 @@ import android.service.notification.NotificationStats; import androidx.annotation.NonNull; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason; @@ -33,10 +34,13 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; import com.android.systemui.statusbar.policy.HeadsUpManager; +import javax.inject.Inject; + /** * Callback for when a user interacts with a {@see ExpandableNotificationRow}. Sends relevant * information about the interaction to the notification pipeline. */ +@SysUISingleton public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback { private final NotificationVisibilityProvider mVisibilityProvider; private final NotifCollection mNotifCollection; @@ -44,6 +48,7 @@ public class OnUserInteractionCallbackImpl implements OnUserInteractionCallback private final StatusBarStateController mStatusBarStateController; private final VisualStabilityCoordinator mVisualStabilityCoordinator; + @Inject public OnUserInteractionCallbackImpl( NotificationVisibilityProvider visibilityProvider, NotifCollection notifCollection, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java index 70b11569f50b..24ef5808b2e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java @@ -22,7 +22,6 @@ import com.android.systemui.Dumpable; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.statusbar.NotificationListener; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -53,7 +52,6 @@ public class NotifPipelineInitializer implements Dumpable { private final NotifInflaterImpl mNotifInflater; private final DumpManager mDumpManager; private final ShadeViewManagerFactory mShadeViewManagerFactory; - private final NotifPipelineFlags mNotifPipelineFlags; @Inject @@ -66,8 +64,7 @@ public class NotifPipelineInitializer implements Dumpable { NotifCoordinators notifCoordinators, NotifInflaterImpl notifInflater, DumpManager dumpManager, - ShadeViewManagerFactory shadeViewManagerFactory, - NotifPipelineFlags notifPipelineFlags + ShadeViewManagerFactory shadeViewManagerFactory ) { mPipelineWrapper = pipelineWrapper; mGroupCoalescer = groupCoalescer; @@ -78,7 +75,6 @@ public class NotifPipelineInitializer implements Dumpable { mDumpManager = dumpManager; mNotifInflater = notifInflater; mShadeViewManagerFactory = shadeViewManagerFactory; - mNotifPipelineFlags = notifPipelineFlags; } /** Hooks the new pipeline up to NotificationManager */ @@ -91,26 +87,22 @@ public class NotifPipelineInitializer implements Dumpable { mDumpManager.registerDumpable("NotifPipeline", this); // Setup inflation - if (mNotifPipelineFlags.isNewPipelineEnabled()) { - mNotifInflater.setRowBinder(rowBinder); - } + mNotifInflater.setRowBinder(rowBinder); // Wire up coordinators mNotifPluggableCoordinators.attach(mPipelineWrapper); // Wire up pipeline - if (mNotifPipelineFlags.isNewPipelineEnabled()) { - mShadeViewManagerFactory - .create(listContainer, stackController) - .attach(mRenderStageManager); - } + mShadeViewManagerFactory + .create(listContainer, stackController) + .attach(mRenderStageManager); mRenderStageManager.attach(mListBuilder); mListBuilder.attach(mNotifCollection); mNotifCollection.attach(mGroupCoalescer); mGroupCoalescer.attach(notificationService); Log.d(TAG, "Notif pipeline initialized." - + " rendering=" + mNotifPipelineFlags.isNewPipelineEnabled()); + + " rendering=" + true); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java deleted file mode 100644 index bdbb0eb48e8a..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/LegacyNotificationPresenterExtensions.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2010 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.notification.collection.legacy; - -import static com.android.systemui.statusbar.phone.CentralSurfaces.SPEW; - -import android.service.notification.StatusBarNotification; -import android.util.Log; - -import androidx.annotation.NonNull; - -import com.android.internal.statusbar.NotificationVisibility; -import com.android.systemui.statusbar.notification.NotificationEntryListener; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; - -import org.jetbrains.annotations.NotNull; - -import javax.inject.Inject; - -/** - * This is some logic extracted from the - * {@link com.android.systemui.statusbar.phone.StatusBarNotificationPresenter} - * into a class that implements a new-pipeline interface so that the new pipeline can implement it - * correctly. - * - * Specifically, this is the logic which updates notifications when uiMode and screen properties - * change, and which closes the shade when the last notification disappears. - */ -public class LegacyNotificationPresenterExtensions implements NotifShadeEventSource { - private static final String TAG = "LegacyNotifPresenter"; - private final NotificationEntryManager mEntryManager; - private boolean mEntryListenerAdded; - private Runnable mShadeEmptiedCallback; - private Runnable mNotifRemovedByUserCallback; - - @Inject - public LegacyNotificationPresenterExtensions(NotificationEntryManager entryManager) { - mEntryManager = entryManager; - } - - private void ensureEntryListenerAdded() { - if (mEntryListenerAdded) return; - mEntryListenerAdded = true; - mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { - @Override - public void onEntryRemoved( - @NotNull NotificationEntry entry, - NotificationVisibility visibility, - boolean removedByUser, - int reason) { - StatusBarNotification old = entry.getSbn(); - if (SPEW) { - Log.d(TAG, "removeNotification key=" + entry.getKey() - + " old=" + old + " reason=" + reason); - } - - if (old != null && !mEntryManager.hasActiveNotifications()) { - if (mShadeEmptiedCallback != null) mShadeEmptiedCallback.run(); - } - if (removedByUser) { - if (mNotifRemovedByUserCallback != null) mNotifRemovedByUserCallback.run(); - } - } - }); - } - - @Override - public void setNotifRemovedByUserCallback(@NonNull Runnable callback) { - if (mNotifRemovedByUserCallback != null) { - throw new IllegalStateException("mNotifRemovedByUserCallback already set"); - } - mNotifRemovedByUserCallback = callback; - ensureEntryListenerAdded(); - } - - @Override - public void setShadeEmptiedCallback(@NonNull Runnable callback) { - if (mShadeEmptiedCallback != null) { - throw new IllegalStateException("mShadeEmptiedCallback already set"); - } - mShadeEmptiedCallback = callback; - ensureEntryListenerAdded(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java deleted file mode 100644 index 103b14b09e9c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnUserInteractionCallbackImplLegacy.java +++ /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.systemui.statusbar.notification.collection.legacy; - -import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; - -import android.service.notification.NotificationListenerService; -import android.service.notification.NotificationStats; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats; -import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; -import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; -import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; -import com.android.systemui.statusbar.policy.HeadsUpManager; - -/** - * Callback for when a user interacts with a {@see ExpandableNotificationRow}. - */ -public class OnUserInteractionCallbackImplLegacy implements OnUserInteractionCallback { - private final NotificationEntryManager mNotificationEntryManager; - private final NotificationVisibilityProvider mVisibilityProvider; - private final HeadsUpManager mHeadsUpManager; - private final StatusBarStateController mStatusBarStateController; - private final VisualStabilityManager mVisualStabilityManager; - private final GroupMembershipManager mGroupMembershipManager; - - public OnUserInteractionCallbackImplLegacy( - NotificationEntryManager notificationEntryManager, - NotificationVisibilityProvider visibilityProvider, - HeadsUpManager headsUpManager, - StatusBarStateController statusBarStateController, - VisualStabilityManager visualStabilityManager, - GroupMembershipManager groupMembershipManager - ) { - mNotificationEntryManager = notificationEntryManager; - mVisibilityProvider = visibilityProvider; - mHeadsUpManager = headsUpManager; - mStatusBarStateController = statusBarStateController; - mVisualStabilityManager = visualStabilityManager; - mGroupMembershipManager = groupMembershipManager; - } - - /** - * Callback triggered when a user: - * 1. Manually dismisses a notification {@see ExpandableNotificationRow}. - * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}. - * {@see StatusBarNotificationActivityStarter} - * - * @param groupSummaryToDismiss the group summary that should be dismissed - * along with this dismissal. If null, does not additionally - * dismiss any notifications. - */ - private void onDismiss( - NotificationEntry entry, - @NotificationListenerService.NotificationCancelReason int cancellationReason, - @Nullable NotificationEntry groupSummaryToDismiss - ) { - int dismissalSurface = NotificationStats.DISMISSAL_SHADE; - if (mHeadsUpManager.isAlerting(entry.getKey())) { - dismissalSurface = NotificationStats.DISMISSAL_PEEK; - } else if (mStatusBarStateController.isDozing()) { - dismissalSurface = NotificationStats.DISMISSAL_AOD; - } - - if (groupSummaryToDismiss != null) { - onDismiss(groupSummaryToDismiss, cancellationReason, null); - } - - mNotificationEntryManager.performRemoveNotification( - entry.getSbn(), - new DismissedByUserStats( - dismissalSurface, - DISMISS_SENTIMENT_NEUTRAL, - mVisibilityProvider.obtain(entry, true)), - cancellationReason - ); - - } - - @Override - public void onImportanceChanged(NotificationEntry entry) { - mVisualStabilityManager.temporarilyAllowReordering(); - } - - /** - * @param entry that is being dismissed - * @return the group summary to dismiss along with this entry if this is the last entry in - * the group. Else, returns null. - */ - @Nullable - private NotificationEntry getGroupSummaryToDismiss(NotificationEntry entry) { - if (mGroupMembershipManager.isOnlyChildInGroup(entry)) { - NotificationEntry groupSummary = mGroupMembershipManager.getLogicalGroupSummary(entry); - return groupSummary.isDismissable() ? groupSummary : null; - } - return null; - } - - @Override - @NonNull - public Runnable registerFutureDismissal(@NonNull NotificationEntry entry, - @CancellationReason int cancellationReason) { - NotificationEntry groupSummaryToDismiss = getGroupSummaryToDismiss(entry); - return () -> onDismiss(entry, cancellationReason, groupSummaryToDismiss); - } -} - diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt index 10a627d65b83..4c406e3ba0b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt @@ -25,8 +25,15 @@ import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.StateName +import com.android.systemui.statusbar.notification.collection.listbuilder.PipelineState.getStateName +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Invalidator +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager +import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable import com.android.systemui.statusbar.notification.logKey import com.android.systemui.util.Compile import javax.inject.Inject @@ -60,68 +67,63 @@ class ShadeListBuilderLogger @Inject constructor( }) } - fun logPreRenderInvalidated(filterName: String, pipelineState: Int) { + private fun logPluggableInvalidated( + type: String, + pluggable: Pluggable<*>, + @StateName pipelineState: Int, + reason: String? + ) { buffer.log(TAG, DEBUG, { - str1 = filterName + str1 = type + str2 = pluggable.name int1 = pipelineState + str3 = reason }, { - """Pre-render Invalidator "$str1" invalidated; pipeline state is $int1""" + """Invalidated while ${getStateName(int1)} by $str1 "$str2" because $str3""" }) } - fun logPreGroupFilterInvalidated(filterName: String, pipelineState: Int) { - buffer.log(TAG, DEBUG, { - str1 = filterName - int1 = pipelineState - }, { - """Pre-group NotifFilter "$str1" invalidated; pipeline state is $int1""" - }) - } + fun logPreRenderInvalidated( + invalidator: Invalidator, + @StateName pipelineState: Int, + reason: String? + ) = logPluggableInvalidated("Pre-render Invalidator", invalidator, pipelineState, reason) - fun logReorderingAllowedInvalidated(name: String, pipelineState: Int) { - buffer.log(TAG, DEBUG, { - str1 = name - int1 = pipelineState - }, { - """ReorderingNowAllowed "$str1" invalidated; pipeline state is $int1""" - }) - } + fun logPreGroupFilterInvalidated( + filter: NotifFilter, + @StateName pipelineState: Int, + reason: String? + ) = logPluggableInvalidated("Pre-group NotifFilter", filter, pipelineState, reason) - fun logPromoterInvalidated(name: String, pipelineState: Int) { - buffer.log(TAG, DEBUG, { - str1 = name - int1 = pipelineState - }, { - """NotifPromoter "$str1" invalidated; pipeline state is $int1""" - }) - } + fun logReorderingAllowedInvalidated( + stabilityManager: NotifStabilityManager, + @StateName pipelineState: Int, + reason: String? + ) = logPluggableInvalidated("ReorderingNowAllowed", stabilityManager, pipelineState, reason) - fun logNotifSectionInvalidated(name: String, pipelineState: Int) { - buffer.log(TAG, DEBUG, { - str1 = name - int1 = pipelineState - }, { - """NotifSection "$str1" invalidated; pipeline state is $int1""" - }) - } + fun logPromoterInvalidated( + promoter: NotifPromoter, + @StateName pipelineState: Int, + reason: String? + ) = logPluggableInvalidated("NotifPromoter", promoter, pipelineState, reason) - fun logNotifComparatorInvalidated(name: String, pipelineState: Int) { - buffer.log(TAG, DEBUG, { - str1 = name - int1 = pipelineState - }, { - """NotifComparator "$str1" invalidated; pipeline state is $int1""" - }) - } + fun logNotifSectionInvalidated( + sectioner: NotifSectioner, + @StateName pipelineState: Int, + reason: String? + ) = logPluggableInvalidated("NotifSection", sectioner, pipelineState, reason) - fun logFinalizeFilterInvalidated(name: String, pipelineState: Int) { - buffer.log(TAG, DEBUG, { - str1 = name - int1 = pipelineState - }, { - """Finalize NotifFilter "$str1" invalidated; pipeline state is $int1""" - }) - } + fun logNotifComparatorInvalidated( + comparator: NotifComparator, + @StateName pipelineState: Int, + reason: String? + ) = logPluggableInvalidated("NotifComparator", comparator, pipelineState, reason) + + fun logFinalizeFilterInvalidated( + filter: NotifFilter, + @StateName pipelineState: Int, + reason: String? + ) = logPluggableInvalidated("Finalize NotifFilter", filter, pipelineState, reason) fun logDuplicateSummary( buildId: Int, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java index b981a9621526..966ab4c61b50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java @@ -49,10 +49,10 @@ public abstract class Pluggable<This> { * Call this method when something has caused this pluggable's behavior to change. The pipeline * will be re-run. */ - public final void invalidateList() { + public final void invalidateList(@Nullable String reason) { if (mListener != null) { Trace.beginSection("Pluggable<" + mName + ">.invalidateList"); - mListener.onPluggableInvalidated((This) this); + mListener.onPluggableInvalidated((This) this, reason); Trace.endSection(); } } @@ -74,7 +74,7 @@ public abstract class Pluggable<This> { * @param <T> The type of pluggable that is being listened to. */ public interface PluggableListener<T> { - /** Called whenever {@link #invalidateList()} is called on this pluggable. */ - void onPluggableInvalidated(T pluggable); + /** Called whenever {@link #invalidateList(String)} is called on this pluggable. */ + void onPluggableInvalidated(T pluggable, @Nullable String reason); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt index 6a1e36f4f469..ec10aaf3cfe3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.collection.provider import com.android.internal.statusbar.NotificationVisibility +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection @@ -25,6 +26,7 @@ import com.android.systemui.statusbar.notification.logging.NotificationLogger import javax.inject.Inject /** pipeline-agnostic implementation for getting [NotificationVisibility]. */ +@SysUISingleton class NotificationVisibilityProviderImpl @Inject constructor( private val notifDataStore: NotifLiveDataStore, private val notifCollection: CommonNotifCollection @@ -46,4 +48,4 @@ class NotificationVisibilityProviderImpl @Inject constructor( NotificationLogger.getNotificationLocation(notifCollection.getEntry(key)) private fun getCount() = notifDataStore.activeNotifCount.value -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java index a2cb9509a621..1b3f83d1892f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/GroupExpansionManagerImpl.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection.render; +import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -28,10 +29,13 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.Set; +import javax.inject.Inject; + /** * Provides grouping information for notification entries including information about a group's * expanded state. */ +@SysUISingleton public class GroupExpansionManagerImpl implements GroupExpansionManager, Coordinator { private final GroupMembershipManager mGroupMembershipManager; private final Set<OnGroupExpansionChangeListener> mOnGroupChangeListeners = new HashSet<>(); @@ -39,6 +43,7 @@ public class GroupExpansionManagerImpl implements GroupExpansionManager, Coordin // Set of summary keys whose groups are expanded private final Set<NotificationEntry> mExpandedGroups = new HashSet<>(); + @Inject public GroupExpansionManagerImpl(GroupMembershipManager groupMembershipManager) { mGroupMembershipManager = groupMembershipManager; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index c9c7fe9e0ab6..bf08fc7f53e2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -35,29 +35,26 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.people.widget.PeopleSpaceWidgetManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.settings.UserContextProvider; +import com.android.systemui.shade.NotifPanelEventsModule; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.notification.AssistantFeedbackController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger; -import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl; import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore; import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotifPipelineChoreographerModule; import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator; -import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator; import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorsModule; import com.android.systemui.statusbar.notification.collection.inflation.BindEventManager; import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl; import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder; import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl; -import com.android.systemui.statusbar.notification.collection.legacy.LegacyNotificationPresenterExtensions; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; -import com.android.systemui.statusbar.notification.collection.legacy.OnUserInteractionCallbackImplLegacy; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider; @@ -88,9 +85,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationSectionsMan import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.NotifPanelEventsModule; import com.android.systemui.statusbar.phone.ShadeController; -import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.wmshell.BubblesManager; @@ -116,12 +111,10 @@ import dagger.Provides; }) public interface NotificationsModule { @Binds - StackScrollAlgorithm.SectionProvider bindSectionProvider( - NotificationSectionsManager impl); + StackScrollAlgorithm.SectionProvider bindSectionProvider(NotificationSectionsManager impl); @Binds - StackScrollAlgorithm.BypassController bindBypassController( - KeyguardBypassController impl); + StackScrollAlgorithm.BypassController bindBypassController(KeyguardBypassController impl); /** Provides an instance of {@link NotificationEntryManager} */ @SysUISingleton @@ -135,7 +128,8 @@ public interface NotificationsModule { LeakDetector leakDetector, IStatusBarService statusBarService, NotifLiveDataStoreImpl notifLiveDataStore, - DumpManager dumpManager) { + DumpManager dumpManager, + @Background Executor bgExecutor) { return new NotificationEntryManager( logger, groupManager, @@ -145,7 +139,8 @@ public interface NotificationsModule { leakDetector, statusBarService, notifLiveDataStore, - dumpManager); + dumpManager, + bgExecutor); } /** Provides an instance of {@link NotificationGutsManager} */ @@ -194,12 +189,8 @@ public interface NotificationsModule { } /** Provides an instance of {@link NotifGutsViewManager} */ - @SysUISingleton - @Provides - static NotifGutsViewManager provideNotifGutsViewManager( - NotificationGutsManager notificationGutsManager) { - return notificationGutsManager; - } + @Binds + NotifGutsViewManager bindNotifGutsViewManager(NotificationGutsManager notificationGutsManager); /** Provides an instance of {@link VisualStabilityManager} */ @SysUISingleton @@ -226,10 +217,8 @@ public interface NotificationsModule { static NotificationLogger provideNotificationLogger( NotificationListener notificationListener, @UiBackground Executor uiBgExecutor, - NotifPipelineFlags notifPipelineFlags, NotifLiveDataStore notifLiveDataStore, NotificationVisibilityProvider visibilityProvider, - NotificationEntryManager entryManager, NotifPipeline notifPipeline, StatusBarStateController statusBarStateController, NotificationLogger.ExpansionStateLogger expansionStateLogger, @@ -237,10 +226,8 @@ public interface NotificationsModule { return new NotificationLogger( notificationListener, uiBgExecutor, - notifPipelineFlags, notifLiveDataStore, visibilityProvider, - entryManager, notifPipeline, statusBarStateController, expansionStateLogger, @@ -257,25 +244,13 @@ public interface NotificationsModule { /** Provides an instance of {@link GroupMembershipManager} */ @SysUISingleton @Provides - static GroupMembershipManager provideGroupMembershipManager( - NotifPipelineFlags notifPipelineFlags, - Lazy<NotificationGroupManagerLegacy> groupManagerLegacy) { - return notifPipelineFlags.isNewPipelineEnabled() - ? new GroupMembershipManagerImpl() - : groupManagerLegacy.get(); + static GroupMembershipManager provideGroupMembershipManager() { + return new GroupMembershipManagerImpl(); } /** Provides an instance of {@link GroupExpansionManager} */ - @SysUISingleton - @Provides - static GroupExpansionManager provideGroupExpansionManager( - NotifPipelineFlags notifPipelineFlags, - Lazy<GroupMembershipManager> groupMembershipManager, - Lazy<NotificationGroupManagerLegacy> groupManagerLegacy) { - return notifPipelineFlags.isNewPipelineEnabled() - ? new GroupExpansionManagerImpl(groupMembershipManager.get()) - : groupManagerLegacy.get(); - } + @Binds + GroupExpansionManager provideGroupExpansionManager(GroupExpansionManagerImpl impl); /** Initializes the notification data pipeline (can be disabled via config). */ @SysUISingleton @@ -294,69 +269,28 @@ public interface NotificationsModule { /** * Provide the active notification collection managing the notifications to render. */ - @Provides - @SysUISingleton - static CommonNotifCollection provideCommonNotifCollection( - NotifPipelineFlags notifPipelineFlags, - Lazy<NotifPipeline> pipeline, - NotificationEntryManager entryManager) { - return notifPipelineFlags.isNewPipelineEnabled() - ? pipeline.get() : entryManager; - } + @Binds + CommonNotifCollection provideCommonNotifCollection(NotifPipeline pipeline); /** * Provide the object which can be used to obtain NotificationVisibility objects. */ @Binds - @SysUISingleton NotificationVisibilityProvider provideNotificationVisibilityProvider( - NotificationVisibilityProviderImpl newProvider); + NotificationVisibilityProviderImpl impl); /** * Provide the active implementation for presenting notifications. */ - @Provides - @SysUISingleton - static NotifShadeEventSource provideNotifShadeEventSource( - NotifPipelineFlags notifPipelineFlags, - Lazy<ShadeEventCoordinator> shadeEventCoordinatorLazy, - Lazy<LegacyNotificationPresenterExtensions> legacyNotificationPresenterExtensionsLazy) { - return notifPipelineFlags.isNewPipelineEnabled() - ? shadeEventCoordinatorLazy.get() - : legacyNotificationPresenterExtensionsLazy.get(); - } + @Binds + NotifShadeEventSource provideNotifShadeEventSource(ShadeEventCoordinator shadeEventCoordinator); /** * Provide a dismissal callback that's triggered when a user manually dismissed a notification * from the notification shade or it gets auto-cancelled by click. */ - @Provides - @SysUISingleton - static OnUserInteractionCallback provideOnUserInteractionCallback( - NotifPipelineFlags notifPipelineFlags, - HeadsUpManager headsUpManager, - StatusBarStateController statusBarStateController, - Lazy<NotifCollection> notifCollection, - Lazy<NotificationVisibilityProvider> visibilityProvider, - Lazy<VisualStabilityCoordinator> visualStabilityCoordinator, - NotificationEntryManager entryManager, - VisualStabilityManager visualStabilityManager, - Lazy<GroupMembershipManager> groupMembershipManagerLazy) { - return notifPipelineFlags.isNewPipelineEnabled() - ? new OnUserInteractionCallbackImpl( - visibilityProvider.get(), - notifCollection.get(), - headsUpManager, - statusBarStateController, - visualStabilityCoordinator.get()) - : new OnUserInteractionCallbackImplLegacy( - entryManager, - visibilityProvider.get(), - headsUpManager, - statusBarStateController, - visualStabilityManager, - groupMembershipManagerLazy.get()); - } + @Binds + OnUserInteractionCallback provideOnUserInteractionCallback(OnUserInteractionCallbackImpl impl); /** */ @Binds diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt index 11ffde625798..0e9f1cda73fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt @@ -23,31 +23,22 @@ import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.Snoo import com.android.systemui.statusbar.NotificationListener import com.android.systemui.statusbar.NotificationPresenter import com.android.systemui.statusbar.notification.AnimatedImageNotificationManager -import com.android.systemui.statusbar.notification.NotifPipelineFlags import com.android.systemui.statusbar.notification.NotificationActivityStarter import com.android.systemui.statusbar.notification.NotificationClicker import com.android.systemui.statusbar.notification.NotificationEntryManager import com.android.systemui.statusbar.notification.NotificationListController import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore import com.android.systemui.statusbar.notification.collection.NotifPipeline -import com.android.systemui.statusbar.notification.collection.NotificationRankingManager import com.android.systemui.statusbar.notification.collection.TargetSdkResolver -import com.android.systemui.statusbar.notification.collection.inflation.BindEventManagerImpl import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection -import com.android.systemui.statusbar.notification.collection.provider.DebugModeFilterProvider import com.android.systemui.statusbar.notification.collection.render.NotifStackController -import com.android.systemui.statusbar.notification.interruption.HeadsUpController import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.phone.CentralSurfaces -import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper import com.android.systemui.statusbar.policy.DeviceProvisionedController -import com.android.systemui.statusbar.policy.HeadsUpManager -import com.android.systemui.statusbar.policy.RemoteInputUriController import com.android.wm.shell.bubbles.Bubbles import dagger.Lazy import java.io.PrintWriter @@ -64,30 +55,21 @@ import javax.inject.Inject @SysUISingleton class NotificationsControllerImpl @Inject constructor( private val centralSurfaces: Lazy<CentralSurfaces>, - private val notifPipelineFlags: NotifPipelineFlags, private val notificationListener: NotificationListener, private val entryManager: NotificationEntryManager, - private val debugModeFilterProvider: DebugModeFilterProvider, - private val legacyRanker: NotificationRankingManager, private val commonNotifCollection: Lazy<CommonNotifCollection>, private val notifPipeline: Lazy<NotifPipeline>, private val notifLiveDataStore: NotifLiveDataStore, private val targetSdkResolver: TargetSdkResolver, - private val newNotifPipelineInitializer: Lazy<NotifPipelineInitializer>, + private val notifPipelineInitializer: Lazy<NotifPipelineInitializer>, private val notifBindPipelineInitializer: NotifBindPipelineInitializer, private val deviceProvisionedController: DeviceProvisionedController, private val notificationRowBinder: NotificationRowBinderImpl, - private val bindEventManagerImpl: BindEventManagerImpl, - private val remoteInputUriController: RemoteInputUriController, - private val groupManagerLegacy: Lazy<NotificationGroupManagerLegacy>, - private val groupAlertTransferHelper: NotificationGroupAlertTransferHelper, - private val headsUpManager: HeadsUpManager, - private val headsUpController: HeadsUpController, private val headsUpViewBinder: HeadsUpViewBinder, private val clickerBuilder: NotificationClicker.Builder, private val animatedImageNotificationManager: AnimatedImageNotificationManager, private val peopleSpaceWidgetManager: PeopleSpaceWidgetManager, - private val bubblesOptional: Optional<Bubbles> + private val bubblesOptional: Optional<Bubbles>, ) : NotificationsController { override fun initialize( @@ -118,33 +100,13 @@ class NotificationsControllerImpl @Inject constructor( notifBindPipelineInitializer.initialize() animatedImageNotificationManager.bind() - if (INITIALIZE_NEW_PIPELINE) { - newNotifPipelineInitializer.get().initialize( - notificationListener, - notificationRowBinder, - listContainer, - stackController) - } + notifPipelineInitializer.get().initialize( + notificationListener, + notificationRowBinder, + listContainer, + stackController) - if (notifPipelineFlags.isNewPipelineEnabled()) { - targetSdkResolver.initialize(notifPipeline.get()) - // TODO - } else { - targetSdkResolver.initialize(entryManager) - remoteInputUriController.attach(entryManager) - groupAlertTransferHelper.bind(entryManager, groupManagerLegacy.get()) - bindEventManagerImpl.attachToLegacyPipeline(entryManager) - headsUpManager.addListener(groupManagerLegacy.get()) - headsUpManager.addListener(groupAlertTransferHelper) - headsUpController.attach(entryManager, headsUpManager) - groupManagerLegacy.get().setHeadsUpManager(headsUpManager) - groupAlertTransferHelper.setHeadsUpManager(headsUpManager) - debugModeFilterProvider.registerInvalidationListener { - entryManager.updateNotifications("debug mode filter changed") - } - - entryManager.initialize(notificationListener, legacyRanker) - } + targetSdkResolver.initialize(notifPipeline.get()) peopleSpaceWidgetManager.attach(notificationListener) } @@ -185,9 +147,4 @@ class NotificationsControllerImpl @Inject constructor( override fun getActiveNotificationsCount(): Int = notifLiveDataStore.activeNotifCount.value - - companion object { - // NOTE: The new pipeline is always active, even if the old pipeline is *rendering*. - private const val INITIALIZE_NEW_PIPELINE = true - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt index 016b388ff60a..99d320d1c7ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.interruption import com.android.systemui.log.LogBuffer import com.android.systemui.log.LogLevel.DEBUG import com.android.systemui.log.LogLevel.INFO +import com.android.systemui.log.LogLevel.WARNING import com.android.systemui.log.dagger.NotificationInterruptLog import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey @@ -211,6 +212,33 @@ class NotificationInterruptLogger @Inject constructor( }) } + fun logNoFullscreen(entry: NotificationEntry, reason: String) { + buffer.log(TAG, DEBUG, { + str1 = entry.logKey + str2 = reason + }, { + "No FullScreenIntent: $str2: $str1" + }) + } + + fun logNoFullscreenWarning(entry: NotificationEntry, reason: String) { + buffer.log(TAG, WARNING, { + str1 = entry.logKey + str2 = reason + }, { + "No FullScreenIntent: WARNING: $str2: $str1" + }) + } + + fun logFullscreen(entry: NotificationEntry, reason: String) { + buffer.log(TAG, DEBUG, { + str1 = entry.logKey + str2 = reason + }, { + "FullScreenIntent: $str2: $str1" + }) + } + fun keyguardHideNotification(entry: NotificationEntry) { buffer.log(TAG, DEBUG, { str1 = entry.logKey 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 8378b69bee9a..535dc6e2b583 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 @@ -177,9 +177,69 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter */ @Override public boolean shouldLaunchFullScreenIntentWhenAdded(NotificationEntry entry) { - return entry.getSbn().getNotification().fullScreenIntent != null - && (!shouldHeadsUp(entry) - || mStatusBarStateController.getState() == StatusBarState.KEYGUARD); + if (entry.getSbn().getNotification().fullScreenIntent == null) { + return false; + } + + // Never show FSI when suppressed by DND + if (entry.shouldSuppressFullScreenIntent()) { + mLogger.logNoFullscreen(entry, "Suppressed by DND"); + return false; + } + + // Never show FSI if importance is not HIGH + if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) { + mLogger.logNoFullscreen(entry, "Not important enough"); + return false; + } + + // If the notification has suppressive GroupAlertBehavior, block FSI and warn. + StatusBarNotification sbn = entry.getSbn(); + if (sbn.isGroup() && sbn.getNotification().suppressAlertingDueToGrouping()) { + // b/231322873: Detect and report an event when a notification has both an FSI and a + // suppressive groupAlertBehavior, and now correctly block the FSI from firing. + final int uid = entry.getSbn().getUid(); + android.util.EventLog.writeEvent(0x534e4554, "231322873", uid, "groupAlertBehavior"); + mLogger.logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN"); + return false; + } + + // If the screen is off, then launch the FullScreenIntent + if (!mPowerManager.isInteractive()) { + mLogger.logFullscreen(entry, "Device is not interactive"); + return true; + } + + // If the device is currently dreaming, then launch the FullScreenIntent + if (isDreaming()) { + mLogger.logFullscreen(entry, "Device is dreaming"); + return true; + } + + // If the keyguard is showing, then launch the FullScreenIntent + if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) { + mLogger.logFullscreen(entry, "Keyguard is showing"); + return true; + } + + // If the notification should HUN, then we don't need FSI + if (shouldHeadsUp(entry)) { + mLogger.logNoFullscreen(entry, "Expected to HUN"); + return false; + } + + // If the notification won't HUN for some other reason (DND/snooze/etc), launch FSI. + mLogger.logFullscreen(entry, "Expected not to HUN"); + return true; + } + + private boolean isDreaming() { + try { + return mDreamManager.isDreaming(); + } catch (RemoteException e) { + Log.e(TAG, "Failed to query dream manager.", e); + return false; + } } private boolean shouldHeadsUpWhenAwake(NotificationEntry entry) { @@ -223,13 +283,7 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter return false; } - boolean isDreaming = false; - try { - isDreaming = mDreamManager.isDreaming(); - } catch (RemoteException e) { - Log.e(TAG, "Failed to query dream manager.", e); - } - boolean inUse = mPowerManager.isScreenOn() && !isDreaming; + boolean inUse = mPowerManager.isScreenOn() && !isDreaming(); if (!inUse) { mLogger.logNoHeadsUpNotInUse(entry); @@ -294,11 +348,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter * @return true if these checks pass, false if the notification should not alert */ private boolean canAlertCommon(NotificationEntry entry) { - if (!mFlags.isNewPipelineEnabled() && mNotificationFilter.shouldFilterOut(entry)) { - mLogger.logNoAlertingFilteredOut(entry); - return false; - } - for (int i = 0; i < mSuppressors.size(); i++) { if (mSuppressors.get(i).suppressInterruptions(entry)) { mLogger.logNoAlertingSuppressedBy(entry, mSuppressors.get(i), /* awake */ false); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java index 9fbd5c39dedd..639187790ae0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java @@ -22,7 +22,6 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.Trace; import android.service.notification.NotificationListenerService; -import android.service.notification.StatusBarNotification; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -39,9 +38,6 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; -import com.android.systemui.statusbar.notification.NotificationEntryListener; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -80,7 +76,6 @@ public class NotificationLogger implements StateListener { private final Executor mUiBgExecutor; private final NotifLiveDataStore mNotifLiveDataStore; private final NotificationVisibilityProvider mVisibilityProvider; - private final NotificationEntryManager mEntryManager; private final NotifPipeline mNotifPipeline; private final NotificationPanelLogger mNotificationPanelLogger; private final ExpansionStateLogger mExpansionStateLogger; @@ -220,10 +215,8 @@ public class NotificationLogger implements StateListener { */ public NotificationLogger(NotificationListener notificationListener, @UiBackground Executor uiBgExecutor, - NotifPipelineFlags notifPipelineFlags, NotifLiveDataStore notifLiveDataStore, NotificationVisibilityProvider visibilityProvider, - NotificationEntryManager entryManager, NotifPipeline notifPipeline, StatusBarStateController statusBarStateController, ExpansionStateLogger expansionStateLogger, @@ -232,7 +225,6 @@ public class NotificationLogger implements StateListener { mUiBgExecutor = uiBgExecutor; mNotifLiveDataStore = notifLiveDataStore; mVisibilityProvider = visibilityProvider; - mEntryManager = entryManager; mNotifPipeline = notifPipeline; mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); @@ -241,36 +233,7 @@ public class NotificationLogger implements StateListener { // Not expected to be destroyed, don't need to unsubscribe statusBarStateController.addCallback(this); - if (notifPipelineFlags.isNewPipelineEnabled()) { - registerNewPipelineListener(); - } else { - registerLegacyListener(); - } - } - - private void registerLegacyListener() { - mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { - @Override - public void onEntryRemoved( - NotificationEntry entry, - NotificationVisibility visibility, - boolean removedByUser, - int reason) { - mExpansionStateLogger.onEntryRemoved(entry.getKey()); - } - - @Override - public void onPreEntryUpdated(NotificationEntry entry) { - mExpansionStateLogger.onEntryUpdated(entry.getKey()); - } - - @Override - public void onInflationError( - StatusBarNotification notification, - Exception exception) { - logNotificationError(notification, exception); - } - }); + registerNewPipelineListener(); } private void registerNewPipelineListener() { @@ -333,26 +296,6 @@ public class NotificationLogger implements StateListener { } } - /** - * Logs Notification inflation error - */ - private void logNotificationError( - StatusBarNotification notification, - Exception exception) { - try { - mBarService.onNotificationError( - notification.getPackageName(), - notification.getTag(), - notification.getId(), - notification.getUid(), - notification.getInitialPid(), - exception.getMessage(), - notification.getUserId()); - } catch (RemoteException ex) { - // The end is nigh. - } - } - private void logNotificationVisibilityChanges( Collection<NotificationVisibility> newlyVisible, Collection<NotificationVisibility> noLongerVisible) { 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 cd4a44e1c6e0..94341ba4bb19 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 @@ -141,7 +141,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f; private static final long RECENTLY_ALERTED_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(30); - private boolean mUpdateSelfBackgroundOnUpdate; + // We don't correctly track dark mode until the content views are inflated, so always update + // the background on first content update just in case it happens to be during a theme change. + private boolean mUpdateSelfBackgroundOnUpdate = true; private boolean mNotificationTranslationFinished = false; private boolean mIsSnoozed; private boolean mIsFaded; 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 2fd02d9f1cd9..defae5b6941f 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 @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification.stack; import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_SCROLL_FLING; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL; import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT; import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE; import static com.android.systemui.util.DumpUtilsKt.println; @@ -5246,6 +5247,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable setClearAllInProgress(true); mShadeNeedsToClose = closeShade; + InteractionJankMonitor.getInstance().begin(this, CUJ_SHADE_CLEAR_ALL); // Decrease the delay for every row we animate to give the sense of // accelerating the swipes final int rowDelayDecrement = 5; @@ -6158,6 +6160,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private void onClearAllAnimationsEnd( List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows) { + InteractionJankMonitor.getInstance().end(CUJ_SHADE_CLEAR_ALL); if (mClearAllAnimationListener != null) { mClearAllAnimationListener.onAnimationEnd(viewsToRemove, selectedRows); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index a0d940e835ff..596b767bb434 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -28,7 +28,6 @@ import static com.android.systemui.statusbar.notification.stack.NotificationStac import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY; import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows; -import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.canChildBeCleared; import static com.android.systemui.statusbar.phone.NotificationIconAreaController.HIGH_PRIORITY; import android.content.res.Configuration; @@ -38,12 +37,10 @@ import android.graphics.PointF; import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; -import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.util.Log; import android.util.Pair; import android.view.Display; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -53,20 +50,17 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.ExpandHelper; import com.android.systemui.Gefingerpoken; import com.android.systemui.R; import com.android.systemui.SwipeHelper; import com.android.systemui.classifier.Classifier; import com.android.systemui.classifier.FalsingCollector; -import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.media.KeyguardMediaController; @@ -75,6 +69,7 @@ import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; @@ -85,9 +80,7 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.LaunchAnimationParameters; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.NotificationEntryListener; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotifCollection; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -118,7 +111,6 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; -import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.DeviceProvisionedController; @@ -163,13 +155,10 @@ public class NotificationStackScrollLayoutController { private final Resources mResources; private final NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder; private final ScrimController mScrimController; - private final NotifPipelineFlags mNotifPipelineFlags; private final NotifPipeline mNotifPipeline; private final NotifCollection mNotifCollection; private final NotificationEntryManager mNotificationEntryManager; - private final IStatusBarService mIStatusBarService; private final UiEventLogger mUiEventLogger; - private final LayoutInflater mLayoutInflater; private final NotificationRemoteInputManager mRemoteInputManager; private final VisualStabilityManager mVisualStabilityManager; private final ShadeController mShadeController; @@ -206,8 +195,6 @@ public class NotificationStackScrollLayoutController { @Nullable private NotificationActivityStarter mNotificationActivityStarter; - private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener; - @VisibleForTesting final View.OnAttachStateChangeListener mOnAttachStateChangeListener = new View.OnAttachStateChangeListener() { @@ -256,10 +243,7 @@ public class NotificationStackScrollLayoutController { mView.setAnimateBottomOnLayout(true); } // Let's update the footer once the notifications have been updated (in the next frame) - mView.post(() -> { - updateFooter(); - updateSectionBoundaries("dynamic privacy changed"); - }); + mView.post(this::updateFooter); }; @VisibleForTesting @@ -634,7 +618,6 @@ public class NotificationStackScrollLayoutController { KeyguardMediaController keyguardMediaController, KeyguardBypassController keyguardBypassController, ZenModeController zenModeController, - SysuiColorExtractor colorExtractor, NotificationLockscreenUserManager lockscreenUserManager, MetricsLogger metricsLogger, DumpManager dumpManager, @@ -647,15 +630,12 @@ public class NotificationStackScrollLayoutController { NotificationGroupManagerLegacy legacyGroupManager, GroupExpansionManager groupManager, @SilentHeader SectionHeaderController silentHeaderController, - NotifPipelineFlags notifPipelineFlags, NotifPipeline notifPipeline, NotifCollection notifCollection, NotificationEntryManager notificationEntryManager, LockscreenShadeTransitionController lockscreenShadeTransitionController, ShadeTransitionController shadeTransitionController, - IStatusBarService iStatusBarService, UiEventLogger uiEventLogger, - LayoutInflater layoutInflater, NotificationRemoteInputManager remoteInputManager, VisualStabilityManager visualStabilityManager, ShadeController shadeController, @@ -698,14 +678,11 @@ public class NotificationStackScrollLayoutController { mCentralSurfaces.requestNotificationUpdate("onGroupsChanged"); } }); - mNotifPipelineFlags = notifPipelineFlags; mSilentHeaderController = silentHeaderController; mNotifPipeline = notifPipeline; mNotifCollection = notifCollection; mNotificationEntryManager = notificationEntryManager; - mIStatusBarService = iStatusBarService; mUiEventLogger = uiEventLogger; - mLayoutInflater = layoutInflater; mRemoteInputManager = remoteInputManager; mVisualStabilityManager = visualStabilityManager; mShadeController = shadeController; @@ -745,21 +722,12 @@ public class NotificationStackScrollLayoutController { .setOnMenuEventListener(mMenuEventListener) .build(); - if (mNotifPipelineFlags.isNewPipelineEnabled()) { - mNotifPipeline.addCollectionListener(new NotifCollectionListener() { - @Override - public void onEntryUpdated(NotificationEntry entry) { - mView.onEntryUpdated(entry); - } - }); - } else { - mNotificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() { - @Override - public void onPreEntryUpdated(NotificationEntry entry) { - mView.onEntryUpdated(entry); - } - }); - } + mNotifPipeline.addCollectionListener(new NotifCollectionListener() { + @Override + public void onEntryUpdated(NotificationEntry entry) { + mView.onEntryUpdated(entry); + } + }); mView.initView(mView.getContext(), mSwipeHelper, mNotificationStackSizeCalculator); mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled()); @@ -1231,10 +1199,6 @@ public class NotificationStackScrollLayoutController { Trace.endSection(); } - public boolean areNotificationsHiddenInShade() { - return mZenModeController.areNotificationsHiddenInShade(); - } - public boolean isShowingEmptyShadeView() { return mShowEmptyShadeView; } @@ -1339,15 +1303,6 @@ public class NotificationStackScrollLayoutController { }; } - public void updateSectionBoundaries(String reason) { - if (mNotifPipelineFlags.isNewPipelineEnabled()) { - return; - } - Trace.beginSection("NSSLC.updateSectionBoundaries"); - mView.updateSectionBoundaries(reason); - Trace.endSection(); - } - public void updateFooter() { Trace.beginSection("NSSLC.updateFooter"); mView.updateFooter(); @@ -1463,39 +1418,18 @@ public class NotificationStackScrollLayoutController { private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove, @SelectedRows int selectedRows) { - if (mNotifPipelineFlags.isNewPipelineEnabled()) { - if (selectedRows == ROWS_ALL) { - mNotifCollection.dismissAllNotifications( - mLockscreenUserManager.getCurrentUserId()); - } else { - final List<Pair<NotificationEntry, DismissedByUserStats>> - entriesWithRowsDismissedFromShade = new ArrayList<>(); - for (ExpandableNotificationRow row : viewsToRemove) { - final NotificationEntry entry = row.getEntry(); - entriesWithRowsDismissedFromShade.add( - new Pair<>(entry, getDismissedByUserStats(entry))); - } - mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade); - } + if (selectedRows == ROWS_ALL) { + mNotifCollection.dismissAllNotifications( + mLockscreenUserManager.getCurrentUserId()); } else { - for (ExpandableNotificationRow rowToRemove : viewsToRemove) { - if (canChildBeCleared(rowToRemove)) { - mNotificationEntryManager.performRemoveNotification( - rowToRemove.getEntry().getSbn(), - getDismissedByUserStats(rowToRemove.getEntry()), - NotificationListenerService.REASON_CANCEL_ALL); - } else { - rowToRemove.resetTranslation(); - } - } - if (selectedRows == ROWS_ALL) { - try { - // TODO(b/169585328): Do not clear media player notifications - mIStatusBarService.onClearAllNotifications( - mLockscreenUserManager.getCurrentUserId()); - } catch (Exception ignored) { - } + final List<Pair<NotificationEntry, DismissedByUserStats>> + entriesWithRowsDismissedFromShade = new ArrayList<>(); + for (ExpandableNotificationRow row : viewsToRemove) { + final NotificationEntry entry = row.getEntry(); + entriesWithRowsDismissedFromShade.add( + new Pair<>(entry, getDismissedByUserStats(entry))); } + mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade); } } 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 d25bbbd2a591..5a8050814c8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -47,6 +47,9 @@ import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper; import com.android.systemui.qs.QSPanelController; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; +import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.statusbar.GestureRecorder; import com.android.systemui.statusbar.LightRevealScrim; import com.android.systemui.statusbar.NotificationPresenter; @@ -265,9 +268,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn boolean isPulsing(); - @Nullable - View getAmbientIndicationContainer(); - boolean isOccluded(); //TODO: These can / should probably be moved to NotificationPresenter or ShadeController @@ -560,8 +560,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn void setLaunchEmergencyActionOnFinishedWaking(boolean launch); - void setTopHidesStatusBar(boolean hides); - QSPanelController getQSPanelController(); boolean areNotificationAlertsDisabled(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java index cb9afe887ab7..53b5b0caf257 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java @@ -55,6 +55,9 @@ import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.qs.QSPanelController; +import com.android.systemui.shade.NotificationPanelView; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DisableFlagsLogger; import com.android.systemui.statusbar.SysuiStatusBarStateController; 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 2daa4759457d..2d6d846bf2a2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -148,7 +148,6 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeController; -import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.emergency.EmergencyGesture; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; @@ -175,6 +174,9 @@ import com.android.systemui.qs.QSPanelController; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.scrim.ScrimView; import com.android.systemui.settings.brightness.BrightnessSliderController; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; +import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.AutoHideUiElement; import com.android.systemui.statusbar.BackDropView; @@ -198,7 +200,6 @@ import com.android.systemui.statusbar.PowerButtonReveal; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.core.StatusBarInitializer; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; @@ -207,7 +208,6 @@ import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; -import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.logging.NotificationLogger; @@ -238,7 +238,6 @@ import com.android.systemui.util.WallpaperController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.MessageRouter; import com.android.systemui.volume.VolumeComponent; -import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.SplashscreenContentDrawer; import com.android.wm.shell.startingsurface.StartingSurface; @@ -307,7 +306,6 @@ public class CentralSurfacesImpl extends CoreStartable implements } private final LockscreenShadeTransitionController mLockscreenShadeTransitionController; - private final DreamOverlayStateController mDreamOverlayStateController; private CentralSurfacesCommandQueueCallbacks mCommandQueueCallbacks; private float mTransitionToFullShadeProgress = 0f; private NotificationListContainer mNotifListContainer; @@ -405,11 +403,6 @@ public class CentralSurfacesImpl extends CoreStartable implements } @Override - public void setTopHidesStatusBar(boolean hides) { - mTopHidesStatusBar = hides; - } - - @Override public QSPanelController getQSPanelController() { return mQSPanelController; } @@ -452,7 +445,6 @@ public class CentralSurfacesImpl extends CoreStartable implements private BiometricUnlockController mBiometricUnlockController; private final LightBarController mLightBarController; private final Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy; - private final LockscreenGestureLogger mLockscreenGestureLogger; @Nullable protected LockscreenWallpaper mLockscreenWallpaper; private final AutoHideController mAutoHideController; @@ -519,9 +511,6 @@ public class CentralSurfacesImpl extends CoreStartable implements private boolean mExpandedVisible; - private final int[] mAbsPos = new int[2]; - - private final NotifShadeEventSource mNotifShadeEventSource; protected final NotificationEntryManager mEntryManager; private final NotificationGutsManager mGutsManager; private final NotificationLogger mNotificationLogger; @@ -603,7 +592,6 @@ public class CentralSurfacesImpl extends CoreStartable implements } } - private Handler mMainHandler; private final DelayableExecutor mMainExecutor; private int mInteractingWindows; @@ -637,12 +625,9 @@ public class CentralSurfacesImpl extends CoreStartable implements // Fingerprint (as computed by getLoggingFingerprint() of the last logged state. private int mLastLoggedStateFingerprint; - private boolean mTopHidesStatusBar; - private boolean mStatusBarWindowHidden; private boolean mIsLaunchingActivityOverLockscreen; private final UserSwitcherController mUserSwitcherController; - private final NetworkController mNetworkController; private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this); protected final BatteryController mBatteryController; protected boolean mPanelExpanded; @@ -662,7 +647,6 @@ public class CentralSurfacesImpl extends CoreStartable implements protected NotificationPresenter mPresenter; private NotificationActivityStarter mNotificationActivityStarter; private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy; - private final Optional<BubblesManager> mBubblesManagerOptional; private final Optional<Bubbles> mBubblesOptional; private final Bubbles.BubbleExpandListener mBubbleExpandListener; private final Optional<StartingSurface> mStartingSurfaceOptional; @@ -704,7 +688,6 @@ public class CentralSurfacesImpl extends CoreStartable implements FalsingManager falsingManager, FalsingCollector falsingCollector, BroadcastDispatcher broadcastDispatcher, - NotifShadeEventSource notifShadeEventSource, NotificationEntryManager notificationEntryManager, NotificationGutsManager notificationGutsManager, NotificationLogger notificationLogger, @@ -719,13 +702,11 @@ public class CentralSurfacesImpl extends CoreStartable implements NotificationLockscreenUserManager lockScreenUserManager, NotificationRemoteInputManager remoteInputManager, UserSwitcherController userSwitcherController, - NetworkController networkController, BatteryController batteryController, SysuiColorExtractor colorExtractor, ScreenLifecycle screenLifecycle, WakefulnessLifecycle wakefulnessLifecycle, SysuiStatusBarStateController statusBarStateController, - Optional<BubblesManager> bubblesManagerOptional, Optional<Bubbles> bubblesOptional, VisualStabilityManager visualStabilityManager, DeviceProvisionedController deviceProvisionedController, @@ -737,7 +718,6 @@ public class CentralSurfacesImpl extends CoreStartable implements DozeParameters dozeParameters, ScrimController scrimController, Lazy<LockscreenWallpaper> lockscreenWallpaperLazy, - LockscreenGestureLogger lockscreenGestureLogger, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, DozeServiceHost dozeServiceHost, PowerManager powerManager, @@ -770,7 +750,6 @@ public class CentralSurfacesImpl extends CoreStartable implements LockscreenShadeTransitionController lockscreenShadeTransitionController, FeatureFlags featureFlags, KeyguardUnlockAnimationController keyguardUnlockAnimationController, - @Main Handler mainHandler, @Main DelayableExecutor delayableExecutor, @Main MessageRouter messageRouter, WallpaperManager wallpaperManager, @@ -779,7 +758,6 @@ public class CentralSurfacesImpl extends CoreStartable implements NotifPipelineFlags notifPipelineFlags, InteractionJankMonitor jankMonitor, DeviceStateManager deviceStateManager, - DreamOverlayStateController dreamOverlayStateController, WiredChargingRippleController wiredChargingRippleController, IDreamManager dreamManager) { super(context); @@ -800,7 +778,6 @@ public class CentralSurfacesImpl extends CoreStartable implements mFalsingCollector = falsingCollector; mFalsingManager = falsingManager; mBroadcastDispatcher = broadcastDispatcher; - mNotifShadeEventSource = notifShadeEventSource; mEntryManager = notificationEntryManager; mGutsManager = notificationGutsManager; mNotificationLogger = notificationLogger; @@ -815,13 +792,11 @@ public class CentralSurfacesImpl extends CoreStartable implements mLockscreenUserManager = lockScreenUserManager; mRemoteInputManager = remoteInputManager; mUserSwitcherController = userSwitcherController; - mNetworkController = networkController; mBatteryController = batteryController; mColorExtractor = colorExtractor; mScreenLifecycle = screenLifecycle; mWakefulnessLifecycle = wakefulnessLifecycle; mStatusBarStateController = statusBarStateController; - mBubblesManagerOptional = bubblesManagerOptional; mBubblesOptional = bubblesOptional; mVisualStabilityManager = visualStabilityManager; mDeviceProvisionedController = deviceProvisionedController; @@ -835,7 +810,6 @@ public class CentralSurfacesImpl extends CoreStartable implements mDozeParameters = dozeParameters; mScrimController = scrimController; mLockscreenWallpaperLazy = lockscreenWallpaperLazy; - mLockscreenGestureLogger = lockscreenGestureLogger; mScreenPinningRequest = screenPinningRequest; mDozeScrimController = dozeScrimController; mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; @@ -862,12 +836,10 @@ public class CentralSurfacesImpl extends CoreStartable implements mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager; mFeatureFlags = featureFlags; mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; - mMainHandler = mainHandler; mMainExecutor = delayableExecutor; mMessageRouter = messageRouter; mWallpaperManager = wallpaperManager; mJankMonitor = jankMonitor; - mDreamOverlayStateController = dreamOverlayStateController; mLockscreenShadeTransitionController = lockscreenShadeTransitionController; mStartingSurfaceOptional = startingSurfaceOptional; @@ -1198,9 +1170,6 @@ public class CentralSurfacesImpl extends CoreStartable implements mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView); mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener()); - if (!mNotifPipelineFlags.isNewPipelineEnabled()) { - mHeadsUpManager.addListener(mVisualStabilityManager); - } mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); createNavigationBar(result); @@ -1484,12 +1453,16 @@ public class CentralSurfacesImpl extends CoreStartable implements mPowerManager.wakeUp( time, PowerManager.WAKE_REASON_GESTURE, "com.android.systemui:" + why); mWakeUpComingFromTouch = true; - where.getLocationInWindow(mTmpInt2); // NOTE, the incoming view can sometimes be the entire container... unsure if // this location is valuable enough - mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2, - mTmpInt2[1] + where.getHeight() / 2); + if (where != null) { + where.getLocationInWindow(mTmpInt2); + mWakeUpTouchLocation = new PointF(mTmpInt2[0] + where.getWidth() / 2, + mTmpInt2[1] + where.getHeight() / 2); + } else { + mWakeUpTouchLocation = new PointF(-1, -1); + } mFalsingCollector.onScreenOnFromTouch(); } } @@ -1736,7 +1709,7 @@ public class CentralSurfacesImpl extends CoreStartable implements // Wrap the animation controller to dismiss the shade and set // mIsLaunchingActivityOverLockscreen during the animation. ActivityLaunchAnimator.Controller delegate = wrapAnimationController( - animationController, dismissShade); + animationController, dismissShade, /* isLaunchForActivity= */ true); controller = new DelegateLaunchAnimatorController(delegate) { @Override public void onIntentStarted(boolean willAnimate) { @@ -1888,12 +1861,6 @@ public class CentralSurfacesImpl extends CoreStartable implements return mDozeServiceHost.isPulsing(); } - @androidx.annotation.Nullable - @Override - public View getAmbientIndicationContainer() { - return mAmbientIndicationContainer; - } - /** * When the keyguard is showing and covered by a "showWhenLocked" activity it * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager} @@ -2273,8 +2240,7 @@ public class CentralSurfacesImpl extends CoreStartable implements public void updateBubblesVisibility() { mBubblesOptional.ifPresent(bubbles -> bubbles.onStatusBarVisibilityChanged( mStatusBarMode != MODE_LIGHTS_OUT - && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT - && !mStatusBarWindowHidden)); + && mStatusBarMode != MODE_LIGHTS_OUT_TRANSPARENT)); } void checkBarMode(@TransitionMode int mode, @WindowVisibleState int windowState, @@ -2491,7 +2457,7 @@ public class CentralSurfacesImpl extends CoreStartable implements true /* isActivityIntent */); ActivityLaunchAnimator.Controller animController = animationController != null ? wrapAnimationController(animationController, - dismissShade) : null; + dismissShade, /* isLaunchForActivity= */ true) : null; // If we animate, we will dismiss the shade only once the animation is done. This is taken // care of by the StatusBarLaunchAnimationController. @@ -2560,13 +2526,34 @@ public class CentralSurfacesImpl extends CoreStartable implements callback.onActivityStarted(ActivityManager.START_CANCELED); } }; + // Do not deferKeyguard when occluded because, when keyguard is occluded, + // we do not launch the activity until keyguard is done. + boolean occluded = mStatusBarKeyguardViewManager.isShowing() + && mStatusBarKeyguardViewManager.isOccluded(); + boolean deferred = !occluded; executeRunnableDismissingKeyguard(runnable, cancelRunnable, dismissShadeDirectly, - willLaunchResolverActivity, true /* deferred */, animate); + willLaunchResolverActivity, deferred /* deferred */, animate); } + /** + * Return a {@link ActivityLaunchAnimator.Controller} wrapping {@code animationController} so + * that: + * - if it launches in the notification shade window and {@code dismissShade} is true, then + * the shade will be instantly dismissed at the end of the animation. + * - if it launches in status bar window, it will make the status bar window match the device + * size during the animation (that way, the animation won't be clipped by the status bar + * size). + * + * @param animationController the controller that is wrapped and will drive the main animation. + * @param dismissShade whether the notification shade will be dismissed at the end of the + * animation. This is ignored if {@code animationController} is not + * animating in the shade window. + * @param isLaunchForActivity whether the launch is for an activity. + */ @Nullable private ActivityLaunchAnimator.Controller wrapAnimationController( - ActivityLaunchAnimator.Controller animationController, boolean dismissShade) { + ActivityLaunchAnimator.Controller animationController, boolean dismissShade, + boolean isLaunchForActivity) { View rootView = animationController.getLaunchContainer().getRootView(); Optional<ActivityLaunchAnimator.Controller> controllerFromStatusBar = @@ -2580,7 +2567,7 @@ public class CentralSurfacesImpl extends CoreStartable implements // If the view is not in the status bar, then we are animating a view in the shade. // We have to make sure that we collapse it when the animation ends or is cancelled. return new StatusBarLaunchAnimatorController(animationController, this, - true /* isLaunchForActivity */); + isLaunchForActivity); } return animationController; @@ -2721,7 +2708,8 @@ public class CentralSurfacesImpl extends CoreStartable implements @Override public void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction, boolean afterKeyguardGone) { - if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP + if (!action.willRunAnimationOnKeyguard() + && mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP && mKeyguardStateController.canDismissLockScreen() && !mStatusBarStateController.leaveOpenOnKeyguardHide() && mDozeServiceHost.isPulsing()) { @@ -3536,6 +3524,9 @@ public class CentralSurfacesImpl extends CoreStartable implements setBouncerShowingForStatusBarComponents(bouncerShowing); mStatusBarHideIconsForBouncerManager.setBouncerShowingAndTriggerUpdate(bouncerShowing); mCommandQueue.recomputeDisableFlags(mDisplayId, true /* animate */); + if (mBouncerShowing) { + wakeUpIfDozing(SystemClock.uptimeMillis(), null, "BOUNCER_VISIBLE"); + } updateScrimController(); if (!mBouncerShowing) { updatePanelExpansionForKeyguard(); @@ -4108,8 +4099,9 @@ public class CentralSurfacesImpl extends CoreStartable implements // We wrap animationCallback with a StatusBarLaunchAnimatorController so that the // shade is collapsed after the animation (or when it is cancelled, aborted, etc). ActivityLaunchAnimator.Controller controller = - animationController != null ? new StatusBarLaunchAnimatorController( - animationController, this, intent.isActivity()) : null; + animationController != null ? wrapAnimationController( + animationController, /* dismissShade= */ true, intent.isActivity()) + : null; mActivityLaunchAnimator.startPendingIntentWithAnimation( controller, animate, intent.getCreatorPackage(), @@ -4355,9 +4347,6 @@ public class CentralSurfacesImpl extends CoreStartable implements Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration()); } - if (!mNotifPipelineFlags.isNewPipelineEnabled()) { - mViewHierarchyManager.updateRowStates(); - } mScreenPinningRequest.onConfigurationChanged(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java index 55b310ff986d..80c3e6ce989d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java @@ -40,6 +40,8 @@ import com.android.systemui.doze.DozeLog; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java index 9863a0ed1ce0..484441a1e76b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceController.java @@ -26,6 +26,7 @@ import com.android.internal.widget.ViewClippingUtil; import com.android.systemui.R; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.HeadsUpStatusBarView; @@ -40,8 +41,8 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; import com.android.systemui.util.ViewController; -import java.util.Optional; import java.util.ArrayList; +import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Consumer; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java index 415bd90ed23a..80432dbd277c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java @@ -217,8 +217,9 @@ public class HeadsUpManagerPhone extends HeadsUpManager implements Dumpable, * Notify that the status bar panel gets expanded or collapsed. * * @param isExpanded True to notify expanded, false to notify collapsed. + * TODO(b/237811427) replace with a listener */ - void setIsPanelExpanded(boolean isExpanded) { + public void setIsPanelExpanded(boolean isExpanded) { if (isExpanded != mIsExpanded) { mIsExpanded = isExpanded; if (isExpanded) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index 42f301d2f222..6bfb0dad28f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -21,6 +21,7 @@ import android.view.MotionEvent; import android.view.ViewConfiguration; import com.android.systemui.Gefingerpoken; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableView; 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 61113a05af16..43a5451f4bb6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java @@ -39,6 +39,7 @@ import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; import android.view.WindowInsets; import android.widget.FrameLayout; import android.widget.ImageView; @@ -62,6 +63,9 @@ import com.android.systemui.qrcodescanner.controller.QRCodeScannerController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.wallet.controller.QuickAccessWalletController; +import java.util.ArrayList; +import java.util.List; + /** * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status * text. @@ -89,7 +93,6 @@ public class KeyguardBottomAreaView extends FrameLayout { private ActivityStarter mActivityStarter; private KeyguardStateController mKeyguardStateController; - private CentralSurfaces mCentralSurfaces; private FalsingManager mFalsingManager; private boolean mDozing; @@ -139,9 +142,38 @@ public class KeyguardBottomAreaView extends FrameLayout { super(context, attrs, defStyleAttr, defStyleRes); } - public void initFrom(KeyguardBottomAreaView oldBottomArea) { - setCentralSurfaces(oldBottomArea.mCentralSurfaces); + /** Initializes the {@link KeyguardBottomAreaView} with the given dependencies */ + public void init( + FalsingManager falsingManager, + QuickAccessWalletController controller, + ControlsComponent controlsComponent, + QRCodeScannerController qrCodeScannerController) { + mFalsingManager = falsingManager; + mQuickAccessWalletController = controller; + mQuickAccessWalletController.setupWalletChangeObservers( + mCardRetriever, WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE); + mQuickAccessWalletController.updateWalletPreference(); + mQuickAccessWalletController.queryWalletCards(mCardRetriever); + updateWalletVisibility(); + mControlsComponent = controlsComponent; + mControlsComponent.getControlsListingController().ifPresent( + c -> c.addCallback(mListingCallback)); + + mQRCodeScannerController = qrCodeScannerController; + mQRCodeScannerController.registerQRCodeScannerChangeObservers( + QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE, + QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE); + updateQRCodeButtonVisibility(); + + updateAffordanceColors(); + } + + /** + * Initializes this instance of {@link KeyguardBottomAreaView} based on the given instance of + * another {@link KeyguardBottomAreaView} + */ + public void initFrom(KeyguardBottomAreaView oldBottomArea) { // if it exists, continue to use the original ambient indication container // instead of the newly inflated one if (mAmbientIndicationArea != null) { @@ -268,10 +300,6 @@ public class KeyguardBottomAreaView extends FrameLayout { updateAffordanceColors(); } - public void setCentralSurfaces(CentralSurfaces centralSurfaces) { - mCentralSurfaces = centralSurfaces; - } - private void updateWalletVisibility() { if (mDozing || mQuickAccessWalletController == null @@ -323,8 +351,17 @@ public class KeyguardBottomAreaView extends FrameLayout { dozeTimeTick(); } - public View getIndicationArea() { - return mIndicationArea; + /** + * Returns a list of animators to use to animate the indication areas. + */ + public List<ViewPropertyAnimator> getIndicationAreaAnimators() { + List<ViewPropertyAnimator> animators = + new ArrayList<>(mAmbientIndicationArea != null ? 2 : 1); + animators.add(mIndicationArea.animate()); + if (mAmbientIndicationArea != null) { + animators.add(mAmbientIndicationArea.animate()); + } + return animators; } @Override @@ -332,7 +369,7 @@ public class KeyguardBottomAreaView extends FrameLayout { return false; } - public void startFinishDozeAnimation() { + private void startFinishDozeAnimation() { long delay = 0; if (mWalletButton.getVisibility() == View.VISIBLE) { startFinishDozeAnimationElement(mWalletButton, delay); @@ -394,9 +431,17 @@ public class KeyguardBottomAreaView extends FrameLayout { } /** - * Sets the alpha of the indication areas and affordances, excluding the lock icon. + * Sets the alpha of various sub-components, for example the indication areas and bottom quick + * action buttons. Does not set the alpha of the lock icon. */ - public void setAffordanceAlpha(float alpha) { + public void setComponentAlphas(float alpha) { + setImportantForAccessibility( + alpha == 0f + ? View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS + : View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + if (mAmbientIndicationArea != null) { + mAmbientIndicationArea.setAlpha(alpha); + } mIndicationArea.setAlpha(alpha); mWalletButton.setAlpha(alpha); mQRCodeScannerButton.setAlpha(alpha); @@ -415,38 +460,6 @@ public class KeyguardBottomAreaView extends FrameLayout { return insets; } - /** Set the falsing manager */ - public void setFalsingManager(FalsingManager falsingManager) { - mFalsingManager = falsingManager; - } - - /** - * Initialize the wallet feature, only enabling if the feature is enabled within the platform. - */ - public void initWallet( - QuickAccessWalletController controller) { - mQuickAccessWalletController = controller; - mQuickAccessWalletController.setupWalletChangeObservers( - mCardRetriever, WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE); - mQuickAccessWalletController.updateWalletPreference(); - mQuickAccessWalletController.queryWalletCards(mCardRetriever); - - updateWalletVisibility(); - updateAffordanceColors(); - } - - /** - * Initialize the qr code scanner feature, controlled by QRCodeScannerController. - */ - public void initQRCodeScanner(QRCodeScannerController qrCodeScannerController) { - mQRCodeScannerController = qrCodeScannerController; - mQRCodeScannerController.registerQRCodeScannerChangeObservers( - QRCodeScannerController.DEFAULT_QR_CODE_SCANNER_CHANGE, - QRCodeScannerController.QR_CODE_SCANNER_PREFERENCE_CHANGE); - updateQRCodeButtonVisibility(); - updateAffordanceColors(); - } - private void updateQRCodeButtonVisibility() { if (mQuickAccessWalletController != null && mQuickAccessWalletController.isWalletEnabled()) { @@ -502,17 +515,6 @@ public class KeyguardBottomAreaView extends FrameLayout { mQRCodeScannerButton.setBackgroundTintList(bgColor); } - /** - * Initialize controls via the ControlsComponent - */ - public void initControls(ControlsComponent controlsComponent) { - mControlsComponent = controlsComponent; - mControlsComponent.getControlsListingController().ifPresent( - c -> c.addCallback(mListingCallback)); - - updateAffordanceColors(); - } - private void onWalletClick(View v) { // More coming here; need to inform the user about how to proceed if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt new file mode 100644 index 000000000000..3942dae7e0c3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt @@ -0,0 +1,34 @@ +/* + * 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.statusbar.phone + +import com.android.systemui.util.ViewController +import javax.inject.Inject + +class KeyguardBottomAreaViewController @Inject constructor(view: KeyguardBottomAreaView) : + ViewController<KeyguardBottomAreaView> (view) { + override fun onViewAttached() { + } + + override fun onViewDetached() { + } + + fun getView(): KeyguardBottomAreaView { + // TODO: remove this method. + return mView + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index 918d6bf93151..0001cd0a2798 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -266,6 +266,9 @@ public class KeyguardBouncer { private void setVisibility(@View.Visibility int visibility) { mContainer.setVisibility(visibility); + if (mKeyguardViewController != null) { + mKeyguardViewController.onBouncerVisibilityChanged(visibility); + } dispatchVisibilityChanged(); } @@ -397,10 +400,6 @@ public class KeyguardBouncer { return mShowingSoon || mExpansion != EXPANSION_HIDDEN && mExpansion != EXPANSION_VISIBLE; } - public boolean getShowingSoon() { - return mShowingSoon; - } - /** * @return {@code true} when bouncer's pre-hide animation already started but isn't completely * hidden yet, {@code false} otherwise. @@ -644,6 +643,10 @@ public class KeyguardBouncer { public interface BouncerExpansionCallback { /** * Invoked when the bouncer expansion reaches {@link KeyguardBouncer#EXPANSION_VISIBLE}. + * This is NOT called each time the bouncer is shown, but rather only when the fully + * shown amount has changed based on the panel expansion. The bouncer's visibility + * can still change when the expansion amount hasn't changed. + * See {@link KeyguardBouncer#isShowing()} for the checks for the bouncer showing state. */ default void onFullyShown() { } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index dde6b168b350..01af48616b65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -27,6 +27,7 @@ import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardStatusView; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.policy.KeyguardUserSwitcherListView; /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index def574c1890f..f06b346780da 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -42,6 +42,7 @@ import com.android.systemui.animation.Interpolators; import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index 2dc3261eb886..a2140c6ab6b7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License */ - package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.HeadsUpAppearanceController.CONTENT_FADE_DELAY; @@ -31,6 +30,7 @@ import android.util.MathUtils; import android.util.Property; import android.view.ContextThemeWrapper; import android.view.View; +import android.view.ViewGroup; import android.view.animation.Interpolator; import androidx.annotation.VisibleForTesting; @@ -40,7 +40,6 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; -import com.android.systemui.statusbar.AlphaOptimizedFrameLayout; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.notification.stack.AnimationFilter; import com.android.systemui.statusbar.notification.stack.AnimationProperties; @@ -54,7 +53,7 @@ import java.util.function.Consumer; * A container for notification icons. It handles overflowing icons properly and positions them * correctly on the screen. */ -public class NotificationIconContainer extends AlphaOptimizedFrameLayout { +public class NotificationIconContainer extends ViewGroup { /** * A float value indicating how much before the overflow start the icons should transform into * a dot. A value of 0 means that they are exactly at the end and a value of 1 means it starts @@ -232,6 +231,31 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } @Override + public boolean hasOverlappingRendering() { + // Does the same as "AlphaOptimizedFrameLayout". + return false; + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + final int childCount = getChildCount(); + final int maxVisibleIcons = getMaxVisibleIcons(childCount); + final int width = MeasureSpec.getSize(widthMeasureSpec); + final int childWidthSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.UNSPECIFIED); + int totalWidth = (int) (getActualPaddingStart() + getActualPaddingEnd()); + for (int i = 0; i < childCount; i++) { + View child = getChildAt(i); + measureChild(child, childWidthSpec, heightMeasureSpec); + if (i <= maxVisibleIcons) { + totalWidth += child.getMeasuredWidth(); + } + } + final int measuredWidth = resolveSize(totalWidth, widthMeasureSpec); + final int measuredHeight = MeasureSpec.getSize(heightMeasureSpec); + setMeasuredDimension(measuredWidth, measuredHeight); + } + + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { float centerY = getHeight() / 2.0f; // we layout all our children on the left at the top @@ -408,8 +432,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { float translationX = getActualPaddingStart(); int firstOverflowIndex = -1; int childCount = getChildCount(); - int maxVisibleIcons = mOnLockScreen ? MAX_ICONS_ON_AOD : - mIsStaticLayout ? MAX_STATIC_ICONS : childCount; + int maxVisibleIcons = getMaxVisibleIcons(childCount); float layoutEnd = getLayoutEnd(); mVisualOverflowStart = 0; mFirstVisibleIconState = null; @@ -493,6 +516,11 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } } + private int getMaxVisibleIcons(int childCount) { + return mOnLockScreen ? MAX_ICONS_ON_AOD : + mIsStaticLayout ? MAX_STATIC_ICONS : childCount; + } + private float getLayoutEnd() { return getActualWidth() - getActualPaddingEnd(); } 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 a8da554d7e1f..905a5f943f0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java @@ -44,6 +44,8 @@ import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.WindowManagerGlobal; +import androidx.lifecycle.ViewTreeLifecycleOwner; + import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dumpable; import com.android.systemui.R; @@ -52,6 +54,7 @@ import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.lifecycle.WindowAddedViewLifecycleOwner; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener; import com.android.systemui.statusbar.NotificationShadeWindowController; @@ -241,6 +244,16 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW mLp.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; mWindowManager.addView(mNotificationShadeView, mLp); + + // Set up and "inject" a LifecycleOwner bound to the Window-View relationship such that all + // views in the sub-tree rooted under this view can access the LifecycleOwner using + // ViewTreeLifecycleOwner.get(...). + if (ViewTreeLifecycleOwner.get(mNotificationShadeView) == null) { + ViewTreeLifecycleOwner.set( + mNotificationShadeView, + new WindowAddedViewLifecycleOwner(mNotificationShadeView)); + } + mLpChanged.copyFrom(mLp); onThemeChanged(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS b/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS index f5828f914eab..4657e9b84550 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/OWNERS @@ -3,14 +3,4 @@ per-file *Notification* = file:../notification/OWNERS per-file NotificationIcon* = ccassidy@google.com, evanlaird@google.com, pixel@google.com -per-file NotificationsQuickSettingsContainer.java = kozynski@google.com, asc@google.com -per-file NotificationsQSContainerController.kt = kozynski@google.com, asc@google.com - per-file NotificationShadeWindowControllerImpl.java = dupin@google.com, cinek@google.com, beverlyt@google.com, pixel@google.com, juliacr@google.com -per-file NotificationShadeWindowViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com -per-file NotificationShadeWindowView.java = pixel@google.com, cinek@google.com, juliacr@google.com - -per-file NotificationPanelUnfoldAnimationController.kt = alexflo@google.com, jeffdq@google.com, juliacr@google.com - -per-file NotificationPanelView.java = pixel@google.com, cinek@google.com, juliacr@google.com -per-file NotificationPanelViewController.java = pixel@google.com, cinek@google.com, juliacr@google.com
\ No newline at end of file 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 d2fc1af010b9..3a85a3ea6391 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -41,6 +41,7 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; import android.view.ViewTreeObserver; import android.view.animation.Interpolator; @@ -66,6 +67,7 @@ import com.android.systemui.util.time.SystemClock; import com.android.wm.shell.animation.FlingAnimationUtils; import java.io.PrintWriter; +import java.util.List; public abstract class PanelViewController { public static final boolean DEBUG = PanelView.DEBUG; @@ -599,9 +601,7 @@ public abstract class PanelViewController { float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) { if (target == mExpandedHeight && mOverExpansion == 0.0f) { // We're at the target and didn't fling and there's no overshoot - endJankMonitoring(CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE); - mKeyguardStateController.notifyPanelFlingEnd(); - notifyExpandingFinished(); + onFlingEnd(false /* cancelled */); return; } mIsFlinging = true; @@ -717,7 +717,7 @@ public abstract class PanelViewController { animator.start(); } - void onFlingEnd(boolean cancelled) { + protected void onFlingEnd(boolean cancelled) { mIsFlinging = false; // No overshoot when the animation ends setOverExpansionInternal(0, false /* isFromGesture */); @@ -1034,16 +1034,19 @@ public abstract class PanelViewController { animator.start(); setAnimator(animator); - View[] viewsToAnimate = { - mKeyguardBottomArea.getIndicationArea(), - mCentralSurfaces.getAmbientIndicationContainer()}; - for (View v : viewsToAnimate) { - if (v == null) { - continue; - } - v.animate().translationY(-mHintDistance).setDuration(250).setInterpolator( - Interpolators.FAST_OUT_SLOW_IN).withEndAction(() -> v.animate().translationY( - 0).setDuration(450).setInterpolator(mBounceInterpolator).start()).start(); + final List<ViewPropertyAnimator> indicationAnimators = + mKeyguardBottomArea.getIndicationAreaAnimators(); + for (final ViewPropertyAnimator indicationAreaAnimator : indicationAnimators) { + indicationAreaAnimator + .translationY(-mHintDistance) + .setDuration(250) + .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) + .withEndAction(() -> indicationAreaAnimator + .translationY(0) + .setDuration(450) + .setInterpolator(mBounceInterpolator) + .start()) + .start(); } } @@ -1110,7 +1113,7 @@ public abstract class PanelViewController { } /** Returns true if {@link PanelView} should be visible. */ - abstract boolean shouldPanelBeVisible(); + abstract protected boolean shouldPanelBeVisible(); /** * Updates the panel expansion and {@link PanelView} visibility if necessary. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java index 2052ee6cdac5..15c6dcfd7889 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java @@ -31,7 +31,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions { private final float mIconAlphaWhenOpaque; - private View mLeftSide, mStatusIcons, mBattery; + private View mStartSide, mStatusIcons, mBattery; private Animator mCurrentAnimation; /** @@ -41,7 +41,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions { super(backgroundView, R.drawable.status_background); final Resources res = statusBarView.getContext().getResources(); mIconAlphaWhenOpaque = res.getFraction(R.dimen.status_bar_icon_drawing_alpha, 1, 1); - mLeftSide = statusBarView.findViewById(R.id.status_bar_left_side); + mStartSide = statusBarView.findViewById(R.id.status_bar_start_side_except_heads_up); mStatusIcons = statusBarView.findViewById(R.id.statusIcons); mBattery = statusBarView.findViewById(R.id.battery); applyModeBackground(-1, getMode(), false /*animate*/); @@ -75,7 +75,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions { } private void applyMode(int mode, boolean animate) { - if (mLeftSide == null) return; // pre-init + if (mStartSide == null) return; // pre-init float newAlpha = getNonBatteryClockAlphaFor(mode); float newAlphaBC = getBatteryClockAlpha(mode); if (mCurrentAnimation != null) { @@ -84,7 +84,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions { if (animate) { AnimatorSet anims = new AnimatorSet(); anims.playTogether( - animateTransitionTo(mLeftSide, newAlpha), + animateTransitionTo(mStartSide, newAlpha), animateTransitionTo(mStatusIcons, newAlpha), animateTransitionTo(mBattery, newAlphaBC) ); @@ -94,7 +94,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions { anims.start(); mCurrentAnimation = anims; } else { - mLeftSide.setAlpha(newAlpha); + mStartSide.setAlpha(newAlpha); mStatusIcons.setAlpha(newAlpha); mBattery.setAlpha(newAlphaBC); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt index 9da2ef734be8..f9c4c8f3b4fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt @@ -21,7 +21,6 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.ViewTreeObserver - import com.android.systemui.R import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController @@ -32,9 +31,7 @@ import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider import com.android.systemui.util.ViewController import com.android.systemui.util.kotlin.getOrNull import com.android.systemui.util.view.ViewUtil - import java.util.Optional - import javax.inject.Inject import javax.inject.Named @@ -58,8 +55,8 @@ class PhoneStatusBarViewController private constructor( override fun onViewAttached() { if (moveFromCenterAnimationController == null) return - val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side) - val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area) + val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_start_side_except_heads_up) + val systemIconArea: ViewGroup = mView.findViewById(R.id.status_bar_end_side_content) val viewsToAnimate = arrayOf( statusBarLeftSide, @@ -126,11 +123,11 @@ class PhoneStatusBarViewController private constructor( class StatusBarViewsCenterProvider : UnfoldMoveFromCenterAnimator.ViewCenterProvider { override fun getViewCenter(view: View, outPoint: Point) = when (view.id) { - R.id.status_bar_left_side -> { + R.id.status_bar_start_side_except_heads_up -> { // items aligned to the start, return start center point getViewEdgeCenter(view, outPoint, isStart = true) } - R.id.system_icon_area -> { + R.id.status_bar_end_side_content -> { // items aligned to the end, return end center point getViewEdgeCenter(view, outPoint, isStart = false) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 0cf9a530afcb..cb0a1480c233 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -54,6 +54,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.scrim.ScrimView; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -783,7 +784,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mInFrontAlpha = 0; } - if (mBouncerHiddenFraction != KeyguardBouncer.EXPANSION_HIDDEN) { + if (mState == ScrimState.DREAMING + && mBouncerHiddenFraction != KeyguardBouncer.EXPANSION_HIDDEN) { final float interpolatedFraction = BouncerPanelExpansionCalculator.aboutToShowBouncerProgress( mBouncerHiddenFraction); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java index cee8b335f380..d37ecbc42168 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java @@ -23,6 +23,8 @@ import android.view.WindowManager; import com.android.systemui.assist.AssistManager; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationShadeWindowController; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt new file mode 100644 index 000000000000..f5ba399fe51a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt @@ -0,0 +1,92 @@ +/* + * 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.statusbar.phone + +import android.graphics.Rect +import android.view.View +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.END_SIDE_CONTENT +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentModule.START_SIDE_CONTENT +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentScope +import com.android.systemui.util.boundsOnScreen +import javax.inject.Inject +import javax.inject.Named + +/** Provides various bounds within the status bar. */ +@StatusBarFragmentScope +class StatusBarBoundsProvider +@Inject +constructor( + private val changeListeners: Set<@JvmSuppressWildcards BoundsChangeListener>, + @Named(START_SIDE_CONTENT) private val startSideContent: View, + @Named(END_SIDE_CONTENT) private val endSideContent: View, +) : StatusBarFragmentComponent.Startable { + + interface BoundsChangeListener { + fun onStatusBarBoundsChanged() + } + + private var previousBounds = + BoundsPair(start = startSideContent.boundsOnScreen, end = endSideContent.boundsOnScreen) + + private val layoutListener = + View.OnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> + val newBounds = BoundsPair(start = visibleStartSideBounds, end = visibleEndSideBounds) + if (previousBounds != newBounds) { + previousBounds = newBounds + changeListeners.forEach { it.onStatusBarBoundsChanged() } + } + } + + override fun start() { + startSideContent.addOnLayoutChangeListener(layoutListener) + endSideContent.addOnLayoutChangeListener(layoutListener) + } + + override fun stop() { + startSideContent.removeOnLayoutChangeListener(layoutListener) + endSideContent.removeOnLayoutChangeListener(layoutListener) + } + + /** + * Returns the bounds of the end side of the status bar that are visible to the user. The end + * side is right when in LTR and is left when in RTL. + * + * Note that even though the layout might be larger, here we only return the bounds for what is + * visible to the user. + * + * The end side of the status bar contains the multi-user switcher and status icons such as + * wi-fi, battery, etc + */ + val visibleEndSideBounds: Rect + get() = endSideContent.boundsOnScreen + + /** + * Returns the bounds of the start side of the status bar that are visible to the user. The + * start side is left when in LTR and is right when in RTL. + * + * Note that even though the layout might be larger, here we only return the bounds for what is + * visible to the user. + * + * The start side of the status bar contains the operator name, clock, on-going call chip, and + * notifications. + */ + val visibleStartSideBounds: Rect + get() = startSideContent.boundsOnScreen +} + +private data class BoundsPair(val start: Rect, val end: Rect) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java index 50f21691b044..ebfbf54abe1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.phone; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index a94c2b73d1f6..7c31366ba4f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.statusbar.phone.StatusBarIconList.Slot; + import android.annotation.NonNull; import android.content.Context; import android.graphics.drawable.Icon; @@ -56,11 +58,12 @@ import javax.inject.Inject; * registered with it. */ @SysUISingleton -public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable, +public class StatusBarIconControllerImpl implements Tunable, ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController, DemoMode { private static final String TAG = "StatusBarIconController"; + private final StatusBarIconList mStatusBarIconList; private final ArrayList<IconManager> mIconGroups = new ArrayList<>(); private final ArraySet<String> mIconHideList = new ArraySet<>(); @@ -74,15 +77,12 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu DemoModeController demoModeController, ConfigurationController configurationController, TunerService tunerService, - DumpManager dumpManager) { - super(context.getResources().getStringArray( - com.android.internal.R.array.config_statusBarIcons)); - configurationController.addCallback(this); - + DumpManager dumpManager, + StatusBarIconList statusBarIconList) { + mStatusBarIconList = statusBarIconList; mContext = context; - loadDimens(); - + configurationController.addCallback(this); commandQueue.addCallback(this); tunerService.addTunable(this, ICON_HIDE_LIST); demoModeController.addCallback(this); @@ -101,15 +101,14 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu group.setController(this); mIconGroups.add(group); - List<Slot> allSlots = getSlots(); + List<Slot> allSlots = mStatusBarIconList.getSlots(); for (int i = 0; i < allSlots.size(); i++) { Slot slot = allSlots.get(i); List<StatusBarIconHolder> holders = slot.getHolderListInViewOrder(); boolean hidden = mIconHideList.contains(slot.getName()); for (StatusBarIconHolder holder : holders) { - int tag = holder.getTag(); - int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag()); + int viewIndex = mStatusBarIconList.getViewIndex(slot.getName(), holder.getTag()); group.onIconAdded(viewIndex, slot.getName(), hidden, holder); } } @@ -144,7 +143,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } mIconHideList.clear(); mIconHideList.addAll(StatusBarIconController.getIconHideList(mContext, newValue)); - ArrayList<Slot> currentSlots = getSlots(); + List<Slot> currentSlots = mStatusBarIconList.getSlots(); ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>(); // This is a little hacky... Peel off all of the holders on all of the slots @@ -163,17 +162,13 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu List<StatusBarIconHolder> iconsForSlot = slotsToReAdd.get(item); if (iconsForSlot == null) continue; for (StatusBarIconHolder holder : iconsForSlot) { - setIcon(getSlotIndex(item.getName()), holder); + setIcon(item.getName(), holder); } } } - private void loadDimens() { - } - - private void addSystemIcon(int index, StatusBarIconHolder holder) { - String slot = getSlotName(index); - int viewIndex = getViewIndex(index, holder.getTag()); + private void addSystemIcon(String slot, StatusBarIconHolder holder) { + int viewIndex = mStatusBarIconList.getViewIndex(slot, holder.getTag()); boolean hidden = mIconHideList.contains(slot); mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, hidden, holder)); @@ -182,18 +177,17 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu /** */ @Override public void setIcon(String slot, int resourceId, CharSequence contentDescription) { - int index = getSlotIndex(slot); - StatusBarIconHolder holder = getIcon(index, 0); + StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0); if (holder == null) { StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), Icon.createWithResource( mContext, resourceId), 0, 0, contentDescription); holder = StatusBarIconHolder.fromIcon(icon); - setIcon(index, holder); + setIcon(slot, holder); } else { holder.getIcon().icon = Icon.createWithResource(mContext, resourceId); holder.getIcon().contentDescription = contentDescription; - handleSet(index, holder); + handleSet(slot, holder); } } @@ -203,21 +197,18 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu */ @Override public void setSignalIcon(String slot, WifiIconState state) { - - int index = getSlotIndex(slot); - if (state == null) { - removeIcon(index, 0); + removeIcon(slot, 0); return; } - StatusBarIconHolder holder = getIcon(index, 0); + StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0); if (holder == null) { holder = StatusBarIconHolder.fromWifiIconState(state); - setIcon(index, holder); + setIcon(slot, holder); } else { holder.setWifiState(state); - handleSet(index, holder); + handleSet(slot, holder); } } @@ -229,8 +220,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu */ @Override public void setMobileIcons(String slot, List<MobileIconState> iconStates) { - Slot mobileSlot = getSlot(slot); - int slotIndex = getSlotIndex(slot); + Slot mobileSlot = mStatusBarIconList.getSlot(slot); // Reverse the sort order to show icons with left to right([Slot1][Slot2]..). // StatusBarIconList has UI design that first items go to the right of second items. @@ -241,10 +231,10 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId); if (holder == null) { holder = StatusBarIconHolder.fromMobileIconState(state); - setIcon(slotIndex, holder); + setIcon(slot, holder); } else { holder.setMobileState(state); - handleSet(slotIndex, holder); + handleSet(slot, holder); } } } @@ -256,21 +246,19 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu */ @Override public void setCallStrengthIcons(String slot, List<CallIndicatorIconState> states) { - Slot callStrengthSlot = getSlot(slot); - int callStrengthSlotIndex = getSlotIndex(slot); + Slot callStrengthSlot = mStatusBarIconList.getSlot(slot); Collections.reverse(states); for (CallIndicatorIconState state : states) { if (!state.isNoCalling) { StatusBarIconHolder holder = callStrengthSlot.getHolderForTag(state.subId); if (holder == null) { holder = StatusBarIconHolder.fromCallIndicatorState(mContext, state); - setIcon(callStrengthSlotIndex, holder); } else { holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), Icon.createWithResource(mContext, state.callStrengthResId), 0, 0, state.callStrengthDescription)); - setIcon(callStrengthSlotIndex, holder); } + setIcon(slot, holder); } setIconVisibility(slot, !state.isNoCalling, state.subId); } @@ -283,21 +271,19 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu */ @Override public void setNoCallingIcons(String slot, List<CallIndicatorIconState> states) { - Slot noCallingSlot = getSlot(slot); - int noCallingSlotIndex = getSlotIndex(slot); + Slot noCallingSlot = mStatusBarIconList.getSlot(slot); Collections.reverse(states); for (CallIndicatorIconState state : states) { if (state.isNoCalling) { StatusBarIconHolder holder = noCallingSlot.getHolderForTag(state.subId); if (holder == null) { holder = StatusBarIconHolder.fromCallIndicatorState(mContext, state); - setIcon(noCallingSlotIndex, holder); } else { holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), Icon.createWithResource(mContext, state.noCallingResId), 0, 0, state.noCallingDescription)); - setIcon(noCallingSlotIndex, holder); } + setIcon(slot, holder); } setIconVisibility(slot, state.isNoCalling, state.subId); } @@ -305,42 +291,31 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu @Override public void setExternalIcon(String slot) { - int viewIndex = getViewIndex(getSlotIndex(slot), 0); + int viewIndex = mStatusBarIconList.getViewIndex(slot, 0); int height = mContext.getResources().getDimensionPixelSize( R.dimen.status_bar_icon_drawing_size); mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height)); } //TODO: remove this (used in command queue and for 3rd party tiles?) - @Override public void setIcon(String slot, StatusBarIcon icon) { - setIcon(getSlotIndex(slot), icon); - } - - /** - * For backwards compatibility, in the event that someone gives us a slot and a status bar icon - */ - private void setIcon(int index, StatusBarIcon icon) { - String slot = getSlotName(index); if (icon == null) { removeAllIconsForSlot(slot); return; } StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon); - setIcon(index, holder); + setIcon(slot, holder); } - /** */ - @Override - public void setIcon(int index, @NonNull StatusBarIconHolder holder) { - boolean isNew = getIcon(index, holder.getTag()) == null; - super.setIcon(index, holder); + private void setIcon(String slot, @NonNull StatusBarIconHolder holder) { + boolean isNew = mStatusBarIconList.getIconHolder(slot, holder.getTag()) == null; + mStatusBarIconList.setIcon(slot, holder); if (isNew) { - addSystemIcon(index, holder); + addSystemIcon(slot, holder); } else { - handleSet(index, holder); + handleSet(slot, holder); } } @@ -351,34 +326,33 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu /** */ public void setIconVisibility(String slot, boolean visibility, int tag) { - int index = getSlotIndex(slot); - StatusBarIconHolder holder = getIcon(index, tag); + StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, tag); if (holder == null || holder.isVisible() == visibility) { return; } holder.setVisible(visibility); - handleSet(index, holder); + handleSet(slot, holder); } /** */ @Override public void setIconAccessibilityLiveRegion(String slotName, int accessibilityLiveRegion) { - Slot slot = getSlot(slotName); + Slot slot = mStatusBarIconList.getSlot(slotName); if (!slot.hasIconsInSlot()) { return; } - int slotIndex = getSlotIndex(slotName); List<StatusBarIconHolder> iconsToUpdate = slot.getHolderListInViewOrder(); for (StatusBarIconHolder holder : iconsToUpdate) { - int viewIndex = getViewIndex(slotIndex, holder.getTag()); + int viewIndex = mStatusBarIconList.getViewIndex(slotName, holder.getTag()); mIconGroups.forEach(l -> l.mGroup.getChildAt(viewIndex) .setAccessibilityLiveRegion(accessibilityLiveRegion)); } } /** */ + @Override public void removeIcon(String slot) { removeAllIconsForSlot(slot); } @@ -386,39 +360,34 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu /** */ @Override public void removeIcon(String slot, int tag) { - removeIcon(getSlotIndex(slot), tag); + if (mStatusBarIconList.getIconHolder(slot, tag) == null) { + return; + } + int viewIndex = mStatusBarIconList.getViewIndex(slot, tag); + mStatusBarIconList.removeIcon(slot, tag); + mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex)); } /** */ @Override public void removeAllIconsForSlot(String slotName) { - Slot slot = getSlot(slotName); + Slot slot = mStatusBarIconList.getSlot(slotName); if (!slot.hasIconsInSlot()) { return; } - int slotIndex = getSlotIndex(slotName); List<StatusBarIconHolder> iconsToRemove = slot.getHolderListInViewOrder(); for (StatusBarIconHolder holder : iconsToRemove) { - int viewIndex = getViewIndex(slotIndex, holder.getTag()); + int viewIndex = mStatusBarIconList.getViewIndex(slotName, holder.getTag()); slot.removeForTag(holder.getTag()); mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex)); } } - /** */ - @Override - public void removeIcon(int index, int tag) { - if (getIcon(index, tag) == null) { - return; - } - super.removeIcon(index, tag); - int viewIndex = getViewIndex(index, 0); - mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex)); - } - private void handleSet(int index, StatusBarIconHolder holder) { - int viewIndex = getViewIndex(index, holder.getTag()); + + private void handleSet(String slotName, StatusBarIconHolder holder) { + int viewIndex = mStatusBarIconList.getViewIndex(slotName, holder.getTag()); mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder)); } @@ -438,7 +407,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } } - super.dump(pw); + mStatusBarIconList.dump(pw); } /** */ @@ -482,7 +451,6 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu /** */ @Override public void onDensityOrFontScaleChanged() { - loadDimens(); refreshIconGroups(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java index c876c3228eb8..8800b05fadb7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java @@ -25,60 +25,72 @@ import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +/** A class holding the list of all the system icons that could be shown in the status bar. */ public class StatusBarIconList { - private ArrayList<Slot> mSlots = new ArrayList<>(); + private final ArrayList<Slot> mSlots = new ArrayList<>(); + private final List<Slot> mViewOnlySlots = Collections.unmodifiableList(mSlots); public StatusBarIconList(String[] slots) { final int N = slots.length; - for (int i=0; i < N; i++) { + for (int i = 0; i < N; i++) { mSlots.add(new Slot(slots[i], null)); } } - public int getSlotIndex(String slot) { - final int N = mSlots.size(); - for (int i=0; i<N; i++) { - Slot item = mSlots.get(i); - if (item.getName().equals(slot)) { - return i; - } - } - // Auto insert new items at the beginning. - mSlots.add(0, new Slot(slot, null)); - return 0; - } - - protected ArrayList<Slot> getSlots() { - return new ArrayList<>(mSlots); - } - - protected Slot getSlot(String name) { - return mSlots.get(getSlotIndex(name)); + /** Returns the list of current slots. */ + public List<Slot> getSlots() { + return mViewOnlySlots; } - public int size() { - return mSlots.size(); + /** + * Gets the slot with the given {@code name}, or creates a new slot if we don't already have a + * slot by that name. + * + * If a new slot is created, that slot will be inserted at the front of the list. + * + * TODO(b/237533036): Rename this to getOrCreateSlot to make it more clear that it could create + * a new slot. Other methods in this class will also create a new slot if we don't have one, + * should those be re-named too? + */ + public Slot getSlot(String name) { + return mSlots.get(findOrInsertSlot(name)); } - public void setIcon(int index, @NonNull StatusBarIconHolder holder) { - mSlots.get(index).addHolder(holder); + /** + * Sets the icon in {@code holder} to be associated with the slot with the given + * {@code slotName}. + */ + public void setIcon(String slotName, @NonNull StatusBarIconHolder holder) { + mSlots.get(findOrInsertSlot(slotName)).addHolder(holder); } - public void removeIcon(int index, int tag) { - mSlots.get(index).removeForTag(tag); + /** + * Removes the icon holder that we had associated with {@code slotName}'s slot at the given + * {@code tag}. + */ + public void removeIcon(String slotName, int tag) { + mSlots.get(findOrInsertSlot(slotName)).removeForTag(tag); } - public String getSlotName(int index) { - return mSlots.get(index).getName(); + /** + * Returns the icon holder currently associated with {@code slotName}'s slot at the given + * {@code tag}, or null if we don't have one. + */ + @Nullable + public StatusBarIconHolder getIconHolder(String slotName, int tag) { + return mSlots.get(findOrInsertSlot(slotName)).getHolderForTag(tag); } - public StatusBarIconHolder getIcon(int index, int tag) { - return mSlots.get(index).getHolderForTag(tag); - } - - public int getViewIndex(int slotIndex, int tag) { + /** + * Returns the index of the icon in {@code slotName}'s slot at the given {@code tag}. + * + * Note that a single slot can have multiple icons, and this function takes that into account. + */ + public int getViewIndex(String slotName, int tag) { + int slotIndex = findOrInsertSlot(slotName); int count = 0; for (int i = 0; i < slotIndex; i++) { Slot item = mSlots.get(i); @@ -100,6 +112,25 @@ public class StatusBarIconList { } } + private int findOrInsertSlot(String slot) { + final int N = mSlots.size(); + for (int i = 0; i < N; i++) { + Slot item = mSlots.get(i); + if (item.getName().equals(slot)) { + return i; + } + } + // Auto insert new items at the beginning. + mSlots.add(0, new Slot(slot, null)); + return 0; + } + + + /** + * A class representing one slot in the status bar system icons view. + * + * Note that one slot can have multiple icons associated with it. + */ public static class Slot { private final String mName; private StatusBarIconHolder mHolder; 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 b6c960a6fa1c..f128a4124c94 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -55,6 +55,7 @@ import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.NotificationMediaManager; @@ -129,8 +130,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void onFullyShown() { mBouncerAnimating = false; updateStates(); - mCentralSurfaces.wakeUpIfDozing(SystemClock.uptimeMillis(), - mCentralSurfaces.getBouncerContainer(), "BOUNCER_VISIBLE"); } @Override @@ -967,7 +966,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public boolean bouncerIsOrWillBeShowing() { - return isBouncerShowing() || mBouncer.getShowingSoon(); + return isBouncerShowing() || mBouncer.inTransit(); } public boolean isFullscreenBouncer() { @@ -1043,7 +1042,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } if (occluded != mLastOccluded || mFirstUpdate) { - mKeyguardUpdateManager.onKeyguardOccludedChanged(occluded); mKeyguardStateController.notifyKeyguardState(showing, occluded); } if ((showing && !occluded) != (mLastShowing && !mLastOccluded) || mFirstUpdate) { @@ -1072,11 +1070,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mCentralSurfaces.onKeyguardViewManagerStatesUpdated(); } - private View getCurrentNavBarView() { - final NavigationBarView navBarView = mCentralSurfaces.getNavigationBarView(); - return navBarView != null ? navBarView.getCurrentView() : null; - } - /** * Updates the visibility of the nav bar window (which will cause insets changes). */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index cf776e3b60d1..451612ad4bb1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -52,21 +52,16 @@ import com.android.systemui.EventLogTags; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.NotificationClickNotifier; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationActivityStarter; -import com.android.systemui.statusbar.notification.NotificationEntryListener; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener; -import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -92,24 +87,19 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte private final Context mContext; - private final CommandQueue mCommandQueue; private final Handler mMainThreadHandler; private final Executor mUiBgExecutor; - private final NotificationEntryManager mEntryManager; - private final NotifPipeline mNotifPipeline; private final NotificationVisibilityProvider mVisibilityProvider; private final HeadsUpManagerPhone mHeadsUpManager; private final ActivityStarter mActivityStarter; private final NotificationClickNotifier mClickNotifier; - private final StatusBarStateController mStatusBarStateController; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private final KeyguardManager mKeyguardManager; private final IDreamManager mDreamManager; private final Optional<BubblesManager> mBubblesManagerOptional; private final Lazy<AssistManager> mAssistManagerLazy; private final NotificationRemoteInputManager mRemoteInputManager; - private final GroupMembershipManager mGroupMembershipManager; private final NotificationLockscreenUserManager mLockscreenUserManager; private final ShadeController mShadeController; private final KeyguardStateController mKeyguardStateController; @@ -118,7 +108,6 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback; private final ActivityIntentHelper mActivityIntentHelper; - private final NotifPipelineFlags mNotifPipelineFlags; private final MetricsLogger mMetricsLogger; private final StatusBarNotificationActivityStarterLogger mLogger; @@ -134,23 +123,19 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte @Inject StatusBarNotificationActivityStarter( Context context, - CommandQueue commandQueue, Handler mainThreadHandler, Executor uiBgExecutor, - NotificationEntryManager entryManager, NotifPipeline notifPipeline, NotificationVisibilityProvider visibilityProvider, HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter, NotificationClickNotifier clickNotifier, - StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, Optional<BubblesManager> bubblesManagerOptional, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, - GroupMembershipManager groupMembershipManager, NotificationLockscreenUserManager lockscreenUserManager, ShadeController shadeController, KeyguardStateController keyguardStateController, @@ -158,7 +143,6 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte LockPatternUtils lockPatternUtils, StatusBarRemoteInputCallback remoteInputCallback, ActivityIntentHelper activityIntentHelper, - NotifPipelineFlags notifPipelineFlags, MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, OnUserInteractionCallback onUserInteractionCallback, @@ -168,23 +152,18 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte ActivityLaunchAnimator activityLaunchAnimator, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) { mContext = context; - mCommandQueue = commandQueue; mMainThreadHandler = mainThreadHandler; mUiBgExecutor = uiBgExecutor; - mEntryManager = entryManager; - mNotifPipeline = notifPipeline; mVisibilityProvider = visibilityProvider; mHeadsUpManager = headsUpManager; mActivityStarter = activityStarter; mClickNotifier = clickNotifier; - mStatusBarStateController = statusBarStateController; mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; mKeyguardManager = keyguardManager; mDreamManager = dreamManager; mBubblesManagerOptional = bubblesManagerOptional; mAssistManagerLazy = assistManagerLazy; mRemoteInputManager = remoteInputManager; - mGroupMembershipManager = groupMembershipManager; mLockscreenUserManager = lockscreenUserManager; mShadeController = shadeController; mKeyguardStateController = keyguardStateController; @@ -192,7 +171,6 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mLockPatternUtils = lockPatternUtils; mStatusBarRemoteInputCallback = remoteInputCallback; mActivityIntentHelper = activityIntentHelper; - mNotifPipelineFlags = notifPipelineFlags; mMetricsLogger = metricsLogger; mLogger = logger; mOnUserInteractionCallback = onUserInteractionCallback; @@ -203,21 +181,12 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mActivityLaunchAnimator = activityLaunchAnimator; mNotificationAnimationProvider = notificationAnimationProvider; - if (!mNotifPipelineFlags.isNewPipelineEnabled()) { - mEntryManager.addNotificationEntryListener(new NotificationEntryListener() { - @Override - public void onPendingEntryAdded(NotificationEntry entry) { - handleFullScreenIntent(entry); - } - }); - } else { - mNotifPipeline.addCollectionListener(new NotifCollectionListener() { - @Override - public void onEntryAdded(NotificationEntry entry) { - handleFullScreenIntent(entry); - } - }); - } + notifPipeline.addCollectionListener(new NotifCollectionListener() { + @Override + public void onEntryAdded(NotificationEntry entry) { + handleFullScreenIntent(entry); + } + }); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java index aa061d74f6c6..4bbd69b91a0a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java @@ -30,19 +30,17 @@ import android.util.Log; import android.util.Slog; import android.view.View; import android.view.accessibility.AccessibilityManager; -import android.widget.TextView; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.widget.MessagingGroup; -import com.android.internal.widget.MessagingMessage; -import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.Dependency; import com.android.systemui.ForegroundServiceNotificationListener; import com.android.systemui.InitController; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -57,7 +55,6 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; @@ -72,16 +69,12 @@ import com.android.systemui.statusbar.notification.stack.NotificationListContain import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.phone.LockscreenGestureLogger.LockscreenUiEvent; import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; -import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import java.util.List; - import javax.inject.Inject; @CentralSurfacesComponent.CentralSurfacesScope class StatusBarNotificationPresenter implements NotificationPresenter, - ConfigurationController.ConfigurationListener, NotificationRowBinderImpl.BindRowCallback, CommandQueue.Callbacks { private static final String TAG = "StatusBarNotificationPresenter"; @@ -92,10 +85,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter, private final NotificationLockscreenUserManager mLockscreenUserManager; private final SysuiStatusBarStateController mStatusBarStateController; private final NotifShadeEventSource mNotifShadeEventSource; - private final NotificationEntryManager mEntryManager; private final NotificationMediaManager mMediaManager; private final NotificationGutsManager mGutsManager; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final LockscreenGestureLogger mLockscreenGestureLogger; private final NotificationPanelViewController mNotificationPanel; @@ -116,9 +107,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, private final IStatusBarService mBarService; private final DynamicPrivacyController mDynamicPrivacyController; private final NotificationListContainer mNotifListContainer; - private boolean mReinflateNotificationsOnUserSwitched; - private boolean mDispatchUiModeChangeOnUserSwitched; - private TextView mNotificationPanelDebugText; protected boolean mVrMode; @@ -143,15 +131,12 @@ class StatusBarNotificationPresenter implements NotificationPresenter, NotificationLockscreenUserManager lockscreenUserManager, SysuiStatusBarStateController sysuiStatusBarStateController, NotifShadeEventSource notifShadeEventSource, - NotificationEntryManager notificationEntryManager, NotificationMediaManager notificationMediaManager, NotificationGutsManager notificationGutsManager, - KeyguardUpdateMonitor keyguardUpdateMonitor, LockscreenGestureLogger lockscreenGestureLogger, InitController initController, NotificationInterruptStateProvider notificationInterruptStateProvider, NotificationRemoteInputManager remoteInputManager, - ConfigurationController configurationController, NotifPipelineFlags notifPipelineFlags, NotificationRemoteInputManager.Callback remoteInputManagerCallback, NotificationListContainer notificationListContainer) { @@ -170,10 +155,8 @@ class StatusBarNotificationPresenter implements NotificationPresenter, mLockscreenUserManager = lockscreenUserManager; mStatusBarStateController = sysuiStatusBarStateController; mNotifShadeEventSource = notifShadeEventSource; - mEntryManager = notificationEntryManager; mMediaManager = notificationMediaManager; mGutsManager = notificationGutsManager; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLockscreenGestureLogger = lockscreenGestureLogger; mAboveShelfObserver = new AboveShelfObserver(stackScrollerController.getView()); mNotificationShadeWindowController = notificationShadeWindowController; @@ -208,13 +191,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, mNotifListContainer); mNotifShadeEventSource.setShadeEmptiedCallback(this::maybeClosePanelForShadeEmptied); mNotifShadeEventSource.setNotifRemovedByUserCallback(this::maybeEndAmbientPulse); - if (!mNotifPipelineFlags.isNewPipelineEnabled()) { - mEntryManager.setUpWithPresenter(this); - mEntryManager.addNotificationLifetimeExtender(mHeadsUpManager); - mEntryManager.addNotificationLifetimeExtender(mGutsManager); - mEntryManager.addNotificationLifetimeExtenders( - remoteInputManager.getLifetimeExtenders()); - } notificationInterruptStateProvider.addSuppressor(mInterruptSuppressor); mLockscreenUserManager.setUpWithPresenter(this); mMediaManager.setUpWithPresenter(this); @@ -227,7 +203,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, onUserSwitched(mLockscreenUserManager.getCurrentUserId()); }); - configurationController.addCallback(this); } /** Called when the shade has been emptied to attempt to close the shade */ @@ -242,65 +217,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, } @Override - public void onDensityOrFontScaleChanged() { - // TODO(b/145659174): Remove legacy pipeline code - if (mNotifPipelineFlags.isNewPipelineEnabled()) return; - MessagingMessage.dropCache(); - MessagingGroup.dropCache(); - if (!mKeyguardUpdateMonitor.isSwitchingUser()) { - updateNotificationsOnDensityOrFontScaleChanged(); - } else { - mReinflateNotificationsOnUserSwitched = true; - } - } - - @Override - public void onUiModeChanged() { - // TODO(b/145659174): Remove legacy pipeline code - if (mNotifPipelineFlags.isNewPipelineEnabled()) return; - if (!mKeyguardUpdateMonitor.isSwitchingUser()) { - updateNotificationsOnUiModeChanged(); - } else { - mDispatchUiModeChangeOnUserSwitched = true; - } - } - - @Override - public void onThemeChanged() { - onDensityOrFontScaleChanged(); - } - - private void updateNotificationsOnUiModeChanged() { - // TODO(b/145659174): Remove legacy pipeline code - if (mNotifPipelineFlags.isNewPipelineEnabled()) return; - List<NotificationEntry> userNotifications = - mEntryManager.getActiveNotificationsForCurrentUser(); - for (int i = 0; i < userNotifications.size(); i++) { - NotificationEntry entry = userNotifications.get(i); - ExpandableNotificationRow row = entry.getRow(); - if (row != null) { - row.onUiModeChanged(); - } - } - } - - private void updateNotificationsOnDensityOrFontScaleChanged() { - // TODO(b/145659174): Remove legacy pipeline code - if (mNotifPipelineFlags.isNewPipelineEnabled()) return; - List<NotificationEntry> userNotifications = - mEntryManager.getActiveNotificationsForCurrentUser(); - for (int i = 0; i < userNotifications.size(); i++) { - NotificationEntry entry = userNotifications.get(i); - entry.onDensityOrFontScaleChanged(); - boolean exposedGuts = entry.areGutsExposed(); - if (exposedGuts) { - mGutsManager.onDensityOrFontScaleChanged(entry); - } - } - } - - - @Override public boolean isCollapsing() { return mNotificationPanel.isCollapsing() || mNotificationShadeWindowController.isLaunchingActivity(); @@ -340,17 +256,6 @@ class StatusBarNotificationPresenter implements NotificationPresenter, // End old BaseStatusBar.userSwitched if (MULTIUSER_DEBUG) mNotificationPanel.setHeaderDebugInfo("USER " + newUserId); mCommandQueue.animateCollapsePanels(); - if (!mNotifPipelineFlags.isNewPipelineEnabled()) { - if (mReinflateNotificationsOnUserSwitched) { - updateNotificationsOnDensityOrFontScaleChanged(); - mReinflateNotificationsOnUserSwitched = false; - } - if (mDispatchUiModeChangeOnUserSwitched) { - updateNotificationsOnUiModeChanged(); - mDispatchUiModeChangeOnUserSwitched = false; - } - updateNotificationViews("user switched"); - } mMediaManager.clearCurrentMediaNotification(); mCentralSurfaces.setLockscreenUser(newUserId); updateMediaMetaData(true, false); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java index 4e9090080c99..75dac1a093dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java @@ -136,8 +136,9 @@ public final class StatusBarTouchableRegionManager implements Dumpable { * Notify that the status bar panel gets expanded or collapsed. * * @param isExpanded True to notify expanded, false to notify collapsed. + * TODO(b/237811427) replace with a listener */ - void setPanelExpanded(boolean isExpanded) { + public void setPanelExpanded(boolean isExpanded) { if (isExpanded != mIsStatusBarExpanded) { mIsStatusBarExpanded = isExpanded; if (isExpanded) { @@ -153,7 +154,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable { * any existing display cutouts (notch) * @return the heads up notification touch area */ - Region calculateTouchableRegion() { + public Region calculateTouchableRegion() { // Update touchable region for HeadsUp notifications final Region headsUpTouchableRegion = mHeadsUpManager.getTouchableRegion(); if (headsUpTouchableRegion != null) { @@ -222,7 +223,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable { } } - void updateRegionForNotch(Region touchableRegion) { + public void updateRegionForNotch(Region touchableRegion) { WindowInsets windowInsets = mNotificationShadeWindowView.getRootWindowInsets(); if (windowInsets == null) { Log.w(TAG, "StatusBarWindowView is not attached."); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java index c5e5297ae6ba..84b279760f36 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java @@ -22,6 +22,9 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; import com.android.keyguard.LockIconViewController; import com.android.systemui.biometrics.AuthRippleController; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; +import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.core.StatusBarInitializer; @@ -33,9 +36,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.CentralSurfacesCommandQueueCallbacks; import com.android.systemui.statusbar.phone.CentralSurfacesImpl; import com.android.systemui.statusbar.phone.LargeScreenShadeHeaderController; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; -import com.android.systemui.statusbar.phone.NotificationShadeWindowView; -import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController; import com.android.systemui.statusbar.phone.StatusBarHeadsUpChangeListener; import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarterModule; import com.android.systemui.statusbar.phone.StatusBarNotificationPresenterModule; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java index 06532c4f9d17..88e985f9eeaf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java @@ -30,10 +30,15 @@ import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.biometrics.AuthRippleView; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.privacy.OngoingPrivacyChip; +import com.android.systemui.shade.NotificationPanelView; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; +import com.android.systemui.shade.NotificationsQuickSettingsContainer; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationShelf; import com.android.systemui.statusbar.NotificationShelfController; @@ -42,11 +47,8 @@ import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; import com.android.systemui.statusbar.phone.NotificationIconAreaController; -import com.android.systemui.statusbar.phone.NotificationPanelView; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; -import com.android.systemui.statusbar.phone.NotificationShadeWindowView; -import com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer; import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarLocationPublisher; @@ -268,7 +270,8 @@ public abstract class StatusBarViewModule { CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, OperatorNameViewController.Factory operatorNameViewControllerFactory, SecureSettings secureSettings, - @Main Executor mainExecutor + @Main Executor mainExecutor, + DumpManager dumpManager ) { return new CollapsedStatusBarFragment(statusBarFragmentComponentFactory, ongoingCallController, @@ -288,6 +291,20 @@ public abstract class StatusBarViewModule { collapsedStatusBarFragmentLogger, operatorNameViewControllerFactory, secureSettings, - mainExecutor); + mainExecutor, + dumpManager); } + + /** + * Constructs a new, unattached {@link KeyguardBottomAreaView}. + * + * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it + */ + @Provides + public static KeyguardBottomAreaView providesKeyguardBottomAreaView( + NotificationPanelView npv, LayoutInflater layoutInflater) { + return (KeyguardBottomAreaView) layoutInflater.inflate(R + .layout.keyguard_bottom_area, npv, false); + } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 597c949168d4..0848729781bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -34,6 +34,8 @@ import android.os.Parcelable; import android.os.UserHandle; import android.provider.Settings; import android.telephony.SubscriptionManager; +import android.util.ArrayMap; +import android.util.IndentingPrintWriter; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; @@ -43,11 +45,14 @@ import android.widget.LinearLayout; import androidx.annotation.VisibleForTesting; +import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DisableFlagsLogger.DisableState; import com.android.systemui.statusbar.OperatorNameView; @@ -59,13 +64,13 @@ import com.android.systemui.statusbar.connectivity.SignalCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationCallback; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.phone.NotificationIconAreaController; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.phone.StatusBarLocationPublisher; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent; +import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; @@ -76,9 +81,12 @@ import com.android.systemui.util.CarrierConfigTracker.CarrierConfigChangedListen import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener; import com.android.systemui.util.settings.SecureSettings; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.Executor; /** @@ -89,7 +97,7 @@ import java.util.concurrent.Executor; @SuppressLint("ValidFragment") public class CollapsedStatusBarFragment extends Fragment implements CommandQueue.Callbacks, StatusBarStateController.StateListener, - SystemStatusAnimationCallback { + SystemStatusAnimationCallback, Dumpable { public static final String TAG = "CollapsedStatusBarFragment"; private static final String EXTRA_PANEL_STATE = "panel_state"; @@ -102,7 +110,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final KeyguardStateController mKeyguardStateController; private final NotificationPanelViewController mNotificationPanelViewController; private final NetworkController mNetworkController; - private LinearLayout mSystemIconArea; + private LinearLayout mEndSideContent; private View mClockView; private View mOngoingCallChip; private View mNotificationIconAreaInner; @@ -124,8 +132,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; private final SecureSettings mSecureSettings; private final Executor mMainExecutor; + private final DumpManager mDumpManager; private List<String> mBlockedIcons = new ArrayList<>(); + private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>(); private SignalCallback mSignalCallback = new SignalCallback() { @Override @@ -185,7 +195,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue CollapsedStatusBarFragmentLogger collapsedStatusBarFragmentLogger, OperatorNameViewController.Factory operatorNameViewControllerFactory, SecureSettings secureSettings, - @Main Executor mainExecutor + @Main Executor mainExecutor, + DumpManager dumpManager ) { mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory; mOngoingCallController = ongoingCallController; @@ -206,6 +217,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mOperatorNameViewControllerFactory = operatorNameViewControllerFactory; mSecureSettings = secureSettings; mMainExecutor = mainExecutor; + mDumpManager = dumpManager; } @Override @@ -217,8 +229,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + mDumpManager.registerDumpable(getClass().getSimpleName(), this); mStatusBarFragmentComponent = mStatusBarFragmentComponentFactory.create(this); mStatusBarFragmentComponent.init(); + mStartableStates.clear(); + for (Startable startable : mStatusBarFragmentComponent.getStartables()) { + mStartableStates.put(startable, Startable.State.STARTING); + startable.start(); + mStartableStates.put(startable, Startable.State.STARTED); + } mStatusBar = (PhoneStatusBarView) view; View contents = mStatusBar.findViewById(R.id.status_bar_contents); @@ -232,16 +251,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mDarkIconManager.setShouldLog(true); updateBlockedIcons(); mStatusBarIconController.addIconGroup(mDarkIconManager); - mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area); + mEndSideContent = mStatusBar.findViewById(R.id.status_bar_end_side_content); mClockView = mStatusBar.findViewById(R.id.clock); mOngoingCallChip = mStatusBar.findViewById(R.id.ongoing_call_chip); - showSystemIconArea(false); + showEndSideContent(false); showClock(false); initEmergencyCryptkeeperText(); initOperatorName(); initNotificationIconArea(); mSystemEventAnimator = - new StatusBarSystemEventAnimator(mSystemIconArea, getResources()); + new StatusBarSystemEventAnimator(mEndSideContent, getResources()); mCarrierConfigTracker.addCallback(mCarrierConfigCallback); mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); } @@ -321,6 +340,13 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue } mCarrierConfigTracker.removeCallback(mCarrierConfigCallback); mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener); + + for (Startable startable : mStatusBarFragmentComponent.getStartables()) { + mStartableStates.put(startable, Startable.State.STOPPING); + startable.stop(); + mStartableStates.put(startable, Startable.State.STOPPED); + } + mDumpManager.unregisterDumpable(getClass().getSimpleName()); } /** Initializes views related to the notification icon area. */ @@ -370,10 +396,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mDisabled2 = state2; if ((diff1 & DISABLE_SYSTEM_INFO) != 0 || ((diff2 & DISABLE2_SYSTEM_ICONS) != 0)) { if ((state1 & DISABLE_SYSTEM_INFO) != 0 || ((state2 & DISABLE2_SYSTEM_ICONS) != 0)) { - hideSystemIconArea(animate); + hideEndSideContent(animate); hideOperatorName(animate); } else { - showSystemIconArea(animate); + showEndSideContent(animate); showOperatorName(animate); } } @@ -474,15 +500,15 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer(); } - private void hideSystemIconArea(boolean animate) { - animateHide(mSystemIconArea, animate); + private void hideEndSideContent(boolean animate) { + animateHide(mEndSideContent, animate); } - private void showSystemIconArea(boolean animate) { + private void showEndSideContent(boolean animate) { // Only show the system icon area if we are not currently animating int state = mAnimationScheduler.getAnimationState(); if (state == IDLE || state == SHOWING_PERSISTENT_DOT) { - animateShow(mSystemIconArea, animate); + animateShow(mEndSideContent, animate); } } @@ -670,4 +696,23 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue updateStatusBarLocation(left, right); } }; + + @Override + public void dump(PrintWriter printWriter, String[] args) { + IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */" "); + StatusBarFragmentComponent component = mStatusBarFragmentComponent; + if (component == null) { + pw.println("StatusBarFragmentComponent is null"); + } else { + Set<Startable> startables = component.getStartables(); + pw.println("Startables: " + startables.size()); + pw.increaseIndent(); + for (Startable startable : startables) { + Startable.State startableState = mStartableStates.getOrDefault(startable, + Startable.State.NONE); + pw.println(startable + ", state: " + startableState); + } + pw.decreaseIndent(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java index 6717bc768fbb..d9a58442d856 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentComponent.java @@ -23,9 +23,12 @@ import com.android.systemui.statusbar.phone.LightsOutNotifController; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; +import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; import com.android.systemui.statusbar.phone.StatusBarDemoMode; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; +import java.util.Set; + import dagger.BindsInstance; import dagger.Subcomponent; @@ -41,7 +44,10 @@ import dagger.Subcomponent; * should be included here or in {@link StatusBarFragmentModule}. */ -@Subcomponent(modules = {StatusBarFragmentModule.class}) +@Subcomponent(modules = { + StatusBarFragmentModule.class, + StatusBarStartablesModule.class +}) @StatusBarFragmentScope public interface StatusBarFragmentComponent { /** Simple factory. */ @@ -52,6 +58,18 @@ public interface StatusBarFragmentComponent { } /** + * Performs initialization logic after {@link StatusBarFragmentComponent} has been constructed. + */ + interface Startable { + void start(); + void stop(); + + enum State { + NONE, STARTING, STARTED, STOPPING, STOPPED + } + } + + /** * Initialize anything extra for the component. Must be called after the component is created. */ default void init() { @@ -92,4 +110,10 @@ public interface StatusBarFragmentComponent { /** */ @StatusBarFragmentScope PhoneStatusBarTransitions getPhoneStatusBarTransitions(); + + /** */ + Set<Startable> getStartables(); + + /** */ + StatusBarBoundsProvider getBoundsProvider(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java index d5f5362eaf3c..41f1f9589ce4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java @@ -21,11 +21,12 @@ import android.view.View; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.dagger.qualifiers.RootView; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.HeadsUpStatusBarView; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; +import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController; @@ -34,12 +35,14 @@ import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.window.StatusBarWindowController; import java.util.Optional; +import java.util.Set; import javax.inject.Named; import dagger.Binds; import dagger.Module; import dagger.Provides; +import dagger.multibindings.Multibinds; /** Dagger module for {@link StatusBarFragmentComponent}. */ @Module @@ -48,6 +51,8 @@ public interface StatusBarFragmentModule { String LIGHTS_OUT_NOTIF_VIEW = "lights_out_notif_view"; String OPERATOR_NAME_VIEW = "operator_name_view"; String OPERATOR_NAME_FRAME_VIEW = "operator_name_frame_view"; + String START_SIDE_CONTENT = "start_side_content"; + String END_SIDE_CONTENT = "end_side_content"; /** */ @Provides @@ -68,6 +73,22 @@ public interface StatusBarFragmentModule { /** */ @Provides @StatusBarFragmentScope + @Named(START_SIDE_CONTENT) + static View startSideContent(@RootView PhoneStatusBarView view) { + return view.findViewById(R.id.status_bar_start_side_content); + } + + /** */ + @Provides + @StatusBarFragmentScope + @Named(END_SIDE_CONTENT) + static View endSideContent(@RootView PhoneStatusBarView view) { + return view.findViewById(R.id.status_bar_end_side_content); + } + + /** */ + @Provides + @StatusBarFragmentScope @Named(LIGHTS_OUT_NOTIF_VIEW) static View provideLightsOutNotifView(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.notification_lights_out); @@ -138,4 +159,8 @@ public interface StatusBarFragmentModule { static HeadsUpStatusBarView providesHeasdUpStatusBarView(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.heads_up_status_bar_view); } + + /** */ + @Multibinds + Set<StatusBarBoundsProvider.BoundsChangeListener> boundsChangeListeners(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt new file mode 100644 index 000000000000..9003d13df0a0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt @@ -0,0 +1,32 @@ +/* + * 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.statusbar.phone.fragment.dagger + +import com.android.systemui.statusbar.phone.StatusBarBoundsProvider +import dagger.Binds +import dagger.Module +import dagger.multibindings.IntoSet + +@Module +internal interface StatusBarStartablesModule { + + @Binds + @IntoSet + fun statusBarBoundsCalculator( + statusBarBoundsProvider: StatusBarBoundsProvider + ): StatusBarFragmentComponent.Startable +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java index 5bd20ff2d090..337ffdf46560 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java @@ -28,9 +28,9 @@ import android.widget.FrameLayout; import com.android.systemui.R; import com.android.systemui.settings.brightness.BrightnessSliderController; import com.android.systemui.settings.brightness.ToggleSlider; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.statusbar.NotificationShadeDepthController; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; -import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import java.util.Objects; import java.util.function.Consumer; @@ -68,6 +68,7 @@ public class BrightnessMirrorController mBrightnessMirror.setVisibility(View.INVISIBLE); }); mVisibilityCallback = visibilityCallback; + updateResources(); } public void showMirror() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java index 2fb16ee9b3b9..bdac88837969 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java @@ -424,11 +424,6 @@ public class KeyguardStateControllerImpl implements KeyguardStateController, Dum } @Override - public void onFaceUnlockStateChanged(boolean running, int userId) { - update(false /* updateAlways */); - } - - @Override public void onStrongAuthStateChanged(int userId) { update(false /* updateAlways */); } 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 f151d291cda3..40281a194b12 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -68,6 +68,7 @@ import com.android.systemui.GuestResetOrExitSessionReceiver; import com.android.systemui.GuestResumeSessionReceiver; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; +import com.android.systemui.animation.DialogCuj; import com.android.systemui.animation.DialogLaunchAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.broadcast.BroadcastSender; @@ -116,6 +117,9 @@ public class UserSwitcherController implements Dumpable { private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000l; + private static final String INTERACTION_JANK_ADD_NEW_USER_TAG = "add_new_user"; + private static final String INTERACTION_JANK_EXIT_GUEST_MODE_TAG = "exit_guest_mode"; + protected final Context mContext; protected final UserTracker mUserTracker; protected final UserManager mUserManager; @@ -597,7 +601,9 @@ public class UserSwitcherController implements Dumpable { } mExitGuestDialog = new ExitGuestDialog(mContext, id, isGuestEphemeral, targetId); if (dialogShower != null) { - dialogShower.showDialog(mExitGuestDialog); + dialogShower.showDialog(mExitGuestDialog, new DialogCuj( + InteractionJankMonitor.CUJ_USER_DIALOG_OPEN, + INTERACTION_JANK_EXIT_GUEST_MODE_TAG)); } else { mExitGuestDialog.show(); } @@ -609,7 +615,11 @@ public class UserSwitcherController implements Dumpable { } mAddUserDialog = new AddUserDialog(mContext); if (dialogShower != null) { - dialogShower.showDialog(mAddUserDialog); + dialogShower.showDialog(mAddUserDialog, + new DialogCuj( + InteractionJankMonitor.CUJ_USER_DIALOG_OPEN, + INTERACTION_JANK_ADD_NEW_USER_TAG + )); } else { mAddUserDialog.show(); } diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java index c99ad23ab23d..fabbb2c1f44d 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIInitializer.java @@ -18,18 +18,20 @@ package com.android.systemui.tv; import android.content.Context; -import com.android.systemui.SystemUIFactory; +import com.android.systemui.SystemUIInitializer; import com.android.systemui.dagger.GlobalRootComponent; /** - * TV variant {@link SystemUIFactory}, that substitutes default {@link GlobalRootComponent} for + * TV variant {@link SystemUIInitializer}, that substitutes default {@link GlobalRootComponent} for * {@link TvGlobalRootComponent} */ -public class TvSystemUIFactory extends SystemUIFactory { +public class TvSystemUIInitializer extends SystemUIInitializer { + public TvSystemUIInitializer(Context context) { + super(context); + } + @Override - protected GlobalRootComponent buildGlobalRootComponent(Context context) { - return DaggerTvGlobalRootComponent.builder() - .context(context) - .build(); + protected GlobalRootComponent.Builder getGlobalRootComponentBuilder() { + return DaggerTvGlobalRootComponent.builder(); } } diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index 9a19d8d11190..f1e89ac81b1b 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -35,6 +35,7 @@ import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; +import com.android.systemui.navigationbar.gestural.GestureModule; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.EnhancedEstimates; @@ -45,6 +46,7 @@ import com.android.systemui.qs.dagger.QSModule; import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsImplementation; +import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationListener; import com.android.systemui.statusbar.NotificationLockscreenUserManager; @@ -87,8 +89,10 @@ import dagger.multibindings.IntoSet; * overridden by the System UI implementation. */ @Module(includes = { + GestureModule.class, PowerModule.class, QSModule.class, + ReferenceScreenshotModule.class, VolumeModule.class, }, subcomponents = { diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt index 7350b37e4b66..13ac39c77c93 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt @@ -18,7 +18,7 @@ package com.android.systemui.unfold import com.android.keyguard.KeyguardUnfoldTransition import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.phone.NotificationPanelUnfoldAnimationController +import com.android.systemui.shade.NotificationPanelUnfoldAnimationController import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt index ad734914170b..74d51112deeb 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt @@ -28,6 +28,7 @@ import android.os.Bundle import android.os.UserManager import android.provider.Settings import android.view.LayoutInflater +import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.widget.AdapterView @@ -38,8 +39,10 @@ import androidx.constraintlayout.helper.widget.Flow import com.android.internal.annotations.VisibleForTesting import com.android.internal.util.UserIcons import com.android.settingslib.Utils +import com.android.systemui.Gefingerpoken import com.android.systemui.R import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.FalsingManager.LOW_PENALTY import com.android.systemui.settings.UserTracker @@ -61,12 +64,13 @@ class UserSwitcherActivity @Inject constructor( private val userSwitcherController: UserSwitcherController, private val broadcastDispatcher: BroadcastDispatcher, private val layoutInflater: LayoutInflater, + private val falsingCollector: FalsingCollector, private val falsingManager: FalsingManager, private val userManager: UserManager, private val userTracker: UserTracker ) : LifecycleActivity() { - private lateinit var parent: ViewGroup + private lateinit var parent: UserSwitcherRootView private lateinit var broadcastReceiver: BroadcastReceiver private var popupMenu: UserSwitcherPopupMenu? = null private lateinit var addButton: View @@ -202,7 +206,14 @@ class UserSwitcherActivity @Inject constructor( or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) - parent = requireViewById<ViewGroup>(R.id.user_switcher_root) + parent = requireViewById<UserSwitcherRootView>(R.id.user_switcher_root) + + parent.touchHandler = object : Gefingerpoken { + override fun onTouchEvent(ev: MotionEvent?): Boolean { + falsingCollector.onTouchEvent(ev) + return false + } + } requireViewById<View>(R.id.cancel).apply { setOnClickListener { @@ -241,7 +252,7 @@ class UserSwitcherActivity @Inject constructor( ) popupMenuAdapter.addAll(items) - popupMenu = UserSwitcherPopupMenu(this, falsingManager).apply { + popupMenu = UserSwitcherPopupMenu(this).apply { setAnchorView(addButton) setAdapter(popupMenuAdapter) setOnItemClickListener { diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt index 754a9342bfb0..ee785b62bd50 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt @@ -23,16 +23,13 @@ import android.view.View.MeasureSpec import android.widget.ListAdapter import android.widget.ListPopupWindow import android.widget.ListView - import com.android.systemui.R -import com.android.systemui.plugins.FalsingManager /** * Popup menu for displaying items on the fullscreen user switcher. */ class UserSwitcherPopupMenu( - private val context: Context, - private val falsingManager: FalsingManager + private val context: Context ) : ListPopupWindow(context) { private val res = context.resources diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt new file mode 100644 index 000000000000..66a301744025 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt @@ -0,0 +1,38 @@ +/* + * 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.user + +import android.content.Context +import android.util.AttributeSet +import android.view.MotionEvent +import androidx.constraintlayout.widget.ConstraintLayout +import com.android.systemui.Gefingerpoken + +/** A simple subclass that allows for observing touch events as they happen. */ +class UserSwitcherRootView( + context: Context, + attrs: AttributeSet? +) : ConstraintLayout(context, attrs) { + + /** Assign this field to observer touch events. */ + var touchHandler: Gefingerpoken? = null + + override fun dispatchTouchEvent(ev: MotionEvent): Boolean { + touchHandler?.onTouchEvent(ev) + return super.dispatchTouchEvent(ev) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt b/packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt new file mode 100644 index 000000000000..80d0e4b90359 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt @@ -0,0 +1,89 @@ +/* + * 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.util + +import android.app.IActivityTaskManager +import android.app.WaitResult +import android.content.Context +import android.content.Intent +import android.os.Bundle +import android.os.UserHandle +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.dagger.qualifiers.UiBackground +import java.util.concurrent.Executor +import javax.inject.Inject + +/** + * Helper class that allows to launch an activity and asynchronously wait + * for it to be launched. This class uses application context, so the intent + * will be launched with FLAG_ACTIVITY_NEW_TASK. + */ +class AsyncActivityLauncher @Inject constructor( + private val context: Context, + private val activityTaskManager: IActivityTaskManager, + @UiBackground private val backgroundExecutor: Executor, + @Main private val mainExecutor: Executor +) { + + private var pendingCallback: ((WaitResult) -> Unit)? = null + + /** + * Starts activity and notifies about the result using the provided [callback]. + * If there is already pending activity launch the call will be ignored. + * + * @return true if launch has started, false otherwise + */ + fun startActivityAsUser(intent: Intent, userHandle: UserHandle, + activityOptions: Bundle? = null, + callback: (WaitResult) -> Unit): Boolean { + if (pendingCallback != null) return false + + pendingCallback = callback + + intent.flags = intent.flags or Intent.FLAG_ACTIVITY_NEW_TASK + + backgroundExecutor.execute { + val waitResult = activityTaskManager.startActivityAndWait( + /* caller = */ null, + /* callingPackage = */ context.packageName, + /* callingFeatureId = */ context.attributionTag, + /* intent = */ intent, + /* resolvedType = */ null, + /* resultTo = */ null, + /* resultWho = */ null, + /* requestCode = */ 0, + /* flags = */ 0, + /* profilerInfo = */ null, + /* options = */ activityOptions, + /* userId = */ userHandle.identifier + ) + mainExecutor.execute { + pendingCallback?.invoke(waitResult) + } + } + + return true + } + + /** + * Cancels pending activity launches. It guarantees that the callback won't be fired + * but the activity will be launched anyway. + */ + fun destroy() { + pendingCallback = null + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt index 57b3f53c48fe..ec7aabb58b49 100644 --- a/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt +++ b/packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt @@ -16,7 +16,9 @@ package com.android.systemui.util +import android.graphics.Rect import android.util.IndentingPrintWriter +import android.view.View import android.view.ViewGroup import java.io.PrintWriter @@ -45,4 +47,12 @@ inline fun PrintWriter.indentIfPossible(block: PrintWriter.() -> Unit) { if (this is IndentingPrintWriter) increaseIndent() block() if (this is IndentingPrintWriter) decreaseIndent() -}
\ No newline at end of file +} + +/** Convenience extension property for [View.getBoundsOnScreen]. */ +val View.boundsOnScreen: Rect + get() { + val bounds = Rect() + getBoundsOnScreen(bounds) + return bounds + } diff --git a/packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt b/packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt new file mode 100644 index 000000000000..f53b6824d45d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt @@ -0,0 +1,38 @@ +/* + * 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.util + +import android.app.ActivityThread +import android.os.Process +import com.android.systemui.dagger.qualifiers.InstrumentationTest +import javax.inject.Inject + +/** + * Used to check whether SystemUI should be fully initialized. + */ +class InitializationChecker @Inject constructor( + @InstrumentationTest private val instrumentationTest: Boolean +) { + + /** + * Only initialize components for the main system ui process running as the primary user + */ + fun initializeComponents(): Boolean = + !instrumentationTest && + Process.myUserHandle().isSystem && + ActivityThread.currentProcessName() == ActivityThread.currentPackageName() +} diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt new file mode 100644 index 000000000000..05d087e232ba --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt @@ -0,0 +1,41 @@ +package com.android.systemui.util.kotlin + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.dagger.qualifiers.Main +import dagger.Module +import dagger.Provides +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers + +/** Providers for various coroutines-related constructs. */ +@Module +object CoroutinesModule { + @Provides + @SysUISingleton + @Application + fun applicationScope( + @Main dispatcher: CoroutineDispatcher, + ): CoroutineScope = CoroutineScope(dispatcher) + + @Provides + @SysUISingleton + @Main + fun mainDispatcher(): CoroutineDispatcher = Dispatchers.Main.immediate + + /** + * Provide a [CoroutineDispatcher] backed by a thread pool containing at most X threads, where + * X is the number of CPU cores available. + * + * Because there are multiple threads at play, there is no serialization order guarantee. You + * should use a [kotlinx.coroutines.channels.Channel] for serialization if necessary. + * + * @see Dispatchers.Default + */ + @Provides + @SysUISingleton + @Background + fun bgDispatcher(): CoroutineDispatcher = Dispatchers.Default +} diff --git a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java index c57dbe37ca5f..064c224b0568 100644 --- a/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java +++ b/packages/SystemUI/src/com/android/systemui/util/service/ObservableServiceConnection.java @@ -155,11 +155,6 @@ public class ObservableServiceConnection<T> implements ServiceConnection { * Disconnect from the service if bound. */ public void unbind() { - if (!mBoundCalled) { - return; - } - mBoundCalled = false; - mContext.unbindService(this); onDisconnected(DISCONNECT_REASON_UNBIND); } @@ -210,12 +205,15 @@ public class ObservableServiceConnection<T> implements ServiceConnection { Log.d(TAG, "onDisconnected:" + reason); } + // If not bound or already unbound, do not proceed setting reason, unbinding, and + // notifying if (!mBoundCalled) { return; } + mBoundCalled = false; mLastDisconnectReason = Optional.of(reason); - unbind(); + mContext.unbindService(this); mProxy = null; applyToCallbacksLocked(callback-> callback.onDisconnected(this, diff --git a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java index 292c062369c1..6e19bed49626 100644 --- a/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java +++ b/packages/SystemUI/src/com/android/systemui/util/service/PersistentConnectionManager.java @@ -72,6 +72,11 @@ public class PersistentConnectionManager<T> { @Override public void onDisconnected(ObservableServiceConnection connection, int reason) { + // Do not attempt to reconnect if we were manually unbound + if (reason == ObservableServiceConnection.DISCONNECT_REASON_UNBIND) { + return; + } + if (mSystemClock.currentTimeMillis() - mStartTime > mMinConnectionDuration) { initiateConnectionAttempt(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java index bca2a24ff12d..708a8ab4fcf6 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/BubblesManager.java @@ -34,7 +34,6 @@ import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.content.pm.UserInfo; -import android.content.res.Configuration; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -72,7 +71,6 @@ import com.android.systemui.statusbar.notification.collection.render.Notificatio 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; import com.android.wm.shell.bubbles.Bubble; @@ -126,7 +124,6 @@ public class BubblesManager implements Dumpable { NotificationShadeWindowController notificationShadeWindowController, KeyguardStateController keyguardStateController, ShadeController shadeController, - ConfigurationController configurationController, @Nullable IStatusBarService statusBarService, INotificationManager notificationManager, NotificationVisibilityProvider visibilityProvider, @@ -145,7 +142,6 @@ public class BubblesManager implements Dumpable { notificationShadeWindowController, keyguardStateController, shadeController, - configurationController, statusBarService, notificationManager, visibilityProvider, @@ -169,7 +165,6 @@ public class BubblesManager implements Dumpable { NotificationShadeWindowController notificationShadeWindowController, KeyguardStateController keyguardStateController, ShadeController shadeController, - ConfigurationController configurationController, @Nullable IStatusBarService statusBarService, INotificationManager notificationManager, NotificationVisibilityProvider visibilityProvider, @@ -213,23 +208,6 @@ public class BubblesManager implements Dumpable { } }); - configurationController.addCallback(new ConfigurationController.ConfigurationListener() { - @Override - public void onConfigChanged(Configuration newConfig) { - mBubbles.onConfigChanged(newConfig); - } - - @Override - public void onUiModeChanged() { - mBubbles.updateForThemeChanges(); - } - - @Override - public void onThemeChanged() { - mBubbles.updateForThemeChanges(); - } - }); - zenModeController.addCallback(new ZenModeController.Callback() { @Override public void onZenChanged(int zen) { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index a6db2aa48a88..83b0022b9f53 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -47,7 +47,6 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; -import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; @@ -56,9 +55,6 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.ShellCommandHandler; -import com.android.wm.shell.compatui.CompatUI; -import com.android.wm.shell.draganddrop.DragAndDrop; -import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; @@ -67,6 +63,7 @@ import com.android.wm.shell.onehanded.OneHandedUiEventLogger; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.protolog.ShellProtoLogImpl; import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.sysui.ShellInterface; import java.io.PrintWriter; import java.util.Arrays; @@ -106,19 +103,16 @@ public final class WMShell extends CoreStartable | SYSUI_STATE_QUICK_SETTINGS_EXPANDED; // Shell interfaces + private final ShellInterface mShell; private final Optional<Pip> mPipOptional; private final Optional<SplitScreen> mSplitScreenOptional; private final Optional<OneHanded> mOneHandedOptional; - private final Optional<HideDisplayCutout> mHideDisplayCutoutOptional; private final Optional<ShellCommandHandler> mShellCommandHandler; - private final Optional<CompatUI> mCompatUIOptional; - private final Optional<DragAndDrop> mDragAndDropOptional; private final CommandQueue mCommandQueue; private final ConfigurationController mConfigurationController; private final KeyguardStateController mKeyguardStateController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final NavigationModeController mNavigationModeController; private final ScreenLifecycle mScreenLifecycle; private final SysUiState mSysUiState; private final WakefulnessLifecycle mWakefulnessLifecycle; @@ -127,26 +121,20 @@ public final class WMShell extends CoreStartable private final Executor mSysUiMainExecutor; private boolean mIsSysUiStateValid; - private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback; - private KeyguardUpdateMonitorCallback mPipKeyguardCallback; private KeyguardUpdateMonitorCallback mOneHandedKeyguardCallback; - private KeyguardStateController.Callback mCompatUIKeyguardCallback; private WakefulnessLifecycle.Observer mWakefulnessObserver; @Inject public WMShell(Context context, + ShellInterface shell, Optional<Pip> pipOptional, Optional<SplitScreen> splitScreenOptional, Optional<OneHanded> oneHandedOptional, - Optional<HideDisplayCutout> hideDisplayCutoutOptional, Optional<ShellCommandHandler> shellCommandHandler, - Optional<CompatUI> sizeCompatUIOptional, - Optional<DragAndDrop> dragAndDropOptional, CommandQueue commandQueue, ConfigurationController configurationController, KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, - NavigationModeController navigationModeController, ScreenLifecycle screenLifecycle, SysUiState sysUiState, ProtoTracer protoTracer, @@ -154,28 +142,50 @@ public final class WMShell extends CoreStartable UserInfoController userInfoController, @Main Executor sysUiMainExecutor) { super(context); + mShell = shell; mCommandQueue = commandQueue; mConfigurationController = configurationController; mKeyguardStateController = keyguardStateController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mNavigationModeController = navigationModeController; mScreenLifecycle = screenLifecycle; mSysUiState = sysUiState; mPipOptional = pipOptional; mSplitScreenOptional = splitScreenOptional; mOneHandedOptional = oneHandedOptional; - mHideDisplayCutoutOptional = hideDisplayCutoutOptional; mWakefulnessLifecycle = wakefulnessLifecycle; mProtoTracer = protoTracer; mShellCommandHandler = shellCommandHandler; - mCompatUIOptional = sizeCompatUIOptional; - mDragAndDropOptional = dragAndDropOptional; mUserInfoController = userInfoController; mSysUiMainExecutor = sysUiMainExecutor; } @Override public void start() { + // Notify with the initial configuration and subscribe for new config changes + mShell.onConfigurationChanged(mContext.getResources().getConfiguration()); + mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() { + @Override + public void onConfigChanged(Configuration newConfig) { + mShell.onConfigurationChanged(newConfig); + } + }); + + // Subscribe to keyguard changes + mKeyguardStateController.addCallback(new KeyguardStateController.Callback() { + @Override + public void onKeyguardShowingChanged() { + mShell.onKeyguardVisibilityChanged(mKeyguardStateController.isShowing(), + mKeyguardStateController.isOccluded(), + mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind()); + } + }); + mKeyguardUpdateMonitor.registerCallback(new KeyguardUpdateMonitorCallback() { + @Override + public void onKeyguardDismissAnimationFinished() { + mShell.onKeyguardDismissAnimationFinished(); + } + }); + // TODO: Consider piping config change and other common calls to a shell component to // delegate internally mProtoTracer.add(this); @@ -183,9 +193,6 @@ public final class WMShell extends CoreStartable mPipOptional.ifPresent(this::initPip); mSplitScreenOptional.ifPresent(this::initSplitScreen); mOneHandedOptional.ifPresent(this::initOneHanded); - mHideDisplayCutoutOptional.ifPresent(this::initHideDisplayCutout); - mCompatUIOptional.ifPresent(this::initCompatUi); - mDragAndDropOptional.ifPresent(this::initDragAndDrop); } @VisibleForTesting @@ -197,42 +204,11 @@ public final class WMShell extends CoreStartable } }); - mPipKeyguardCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - pip.onKeyguardVisibilityChanged(showing, - mKeyguardStateController.isAnimatingBetweenKeyguardAndSurfaceBehind()); - } - - @Override - public void onKeyguardDismissAnimationFinished() { - pip.onKeyguardDismissAnimationFinished(); - } - }; - mKeyguardUpdateMonitor.registerCallback(mPipKeyguardCallback); - mSysUiState.addCallback(sysUiStateFlag -> { mIsSysUiStateValid = (sysUiStateFlag & INVALID_SYSUI_STATE_MASK) == 0; pip.onSystemUiStateChanged(mIsSysUiStateValid, sysUiStateFlag); }); - mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() { - @Override - public void onConfigChanged(Configuration newConfig) { - pip.onConfigurationChanged(newConfig); - } - - @Override - public void onDensityOrFontScaleChanged() { - pip.onDensityOrFontScaleChanged(); - } - - @Override - public void onThemeChanged() { - pip.onOverlayChanged(); - } - }); - // The media session listener needs to be re-registered when switching users mUserInfoController.addCallback((String name, Drawable picture, String userAccount) -> pip.registerSessionListenerForCurrentUser()); @@ -240,14 +216,6 @@ public final class WMShell extends CoreStartable @VisibleForTesting void initSplitScreen(SplitScreen splitScreen) { - mSplitScreenKeyguardCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - splitScreen.onKeyguardVisibilityChanged(showing); - } - }; - mKeyguardUpdateMonitor.registerCallback(mSplitScreenKeyguardCallback); - mWakefulnessLifecycle.addObserver(new WakefulnessLifecycle.Observer() { @Override public void onFinishedWakingUp() { @@ -293,14 +261,9 @@ public final class WMShell extends CoreStartable } }); + // TODO: Either move into ShellInterface or register a receiver on the Shell side directly mOneHandedKeyguardCallback = new KeyguardUpdateMonitorCallback() { @Override - public void onKeyguardVisibilityChanged(boolean showing) { - oneHanded.onKeyguardVisibilityChanged(showing); - oneHanded.stopOneHanded(); - } - - @Override public void onUserSwitchComplete(int userId) { oneHanded.onUserSwitch(userId); } @@ -348,48 +311,6 @@ public final class WMShell extends CoreStartable } } }); - - mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() { - @Override - public void onConfigChanged(Configuration newConfig) { - oneHanded.onConfigChanged(newConfig); - } - }); - } - - @VisibleForTesting - void initHideDisplayCutout(HideDisplayCutout hideDisplayCutout) { - mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() { - @Override - public void onConfigChanged(Configuration newConfig) { - hideDisplayCutout.onConfigurationChanged(newConfig); - } - }); - } - - @VisibleForTesting - void initCompatUi(CompatUI sizeCompatUI) { - mCompatUIKeyguardCallback = new KeyguardStateController.Callback() { - @Override - public void onKeyguardShowingChanged() { - sizeCompatUI.onKeyguardShowingChanged(mKeyguardStateController.isShowing()); - } - }; - mKeyguardStateController.addCallback(mCompatUIKeyguardCallback); - } - - void initDragAndDrop(DragAndDrop dragAndDrop) { - mConfigurationController.addCallback(new ConfigurationController.ConfigurationListener() { - @Override - public void onConfigChanged(Configuration newConfig) { - dragAndDrop.onConfigChanged(newConfig); - } - - @Override - public void onThemeChanged() { - dragAndDrop.onThemeChanged(); - } - }); } @Override diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java index ac1a83c269e0..4021652295c1 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java @@ -20,6 +20,7 @@ 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.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -31,6 +32,7 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableResources; import android.view.Gravity; +import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; @@ -42,6 +44,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -104,6 +107,17 @@ public class KeyguardHostViewControllerTest extends SysuiTestCase { } @Test + public void onBouncerVisible_propagatesToKeyguardSecurityContainerController() { + mKeyguardHostViewController.onBouncerVisibilityChanged(ViewGroup.VISIBLE); + mKeyguardHostViewController.onBouncerVisibilityChanged(ViewGroup.INVISIBLE); + + InOrder order = inOrder(mKeyguardSecurityContainerController); + order.verify(mKeyguardSecurityContainerController).onBouncerVisibilityChanged(View.VISIBLE); + order.verify(mKeyguardSecurityContainerController).onBouncerVisibilityChanged( + View.INVISIBLE); + } + + @Test public void testGravityReappliedOnConfigurationChange() { FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt index b1e2012ecd40..ad6d146fb5f3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt @@ -92,7 +92,6 @@ private fun faceModel(user: Int) = KeyguardFaceListenModel( keyguardAwake = false, keyguardGoingAway = false, listeningForFaceAssistant = false, - lockIconPressed = false, occludingAppRequestingFaceAuth = false, primaryUser = false, scanningAllowedByStrongAuth = false, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java index bc351427310d..68e49c0a1d4b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -25,17 +25,21 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.res.Configuration; import android.content.res.Resources; +import android.hardware.biometrics.BiometricSourceType; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; +import android.view.View; import android.view.WindowInsetsController; import androidx.test.filters.SmallTest; @@ -46,6 +50,7 @@ import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.biometrics.SidefpsController; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.log.SessionTracker; @@ -59,10 +64,14 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.Optional; + @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper() @@ -124,6 +133,14 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { private SessionTracker mSessionTracker; @Mock private KeyguardViewController mKeyguardViewController; + @Mock + private SidefpsController mSidefpsController; + @Mock + private KeyguardPasswordViewController mKeyguardPasswordViewControllerMock; + + @Captor + private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback; + private Configuration mConfiguration; private KeyguardSecurityContainerController mKeyguardSecurityContainerController; @@ -160,7 +177,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { mKeyguardStateController, mKeyguardSecurityViewFlipperController, mConfigurationController, mFalsingCollector, mFalsingManager, mUserSwitcherController, mFeatureFlags, mGlobalSettings, - mSessionTracker).create(mSecurityCallback); + mSessionTracker, Optional.of(mSidefpsController)).create(mSecurityCallback); } @Test @@ -258,9 +275,7 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { @Test public void showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() { when(mResources.getBoolean(R.bool.can_use_one_handed_bouncer)).thenReturn(true); - when(mKeyguardSecurityViewFlipperController.getSecurityView( - eq(SecurityMode.Password), any(KeyguardSecurityCallback.class))) - .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewController); + setupGetSecurityView(); mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Password); verify(mView).initMode(MODE_DEFAULT, mGlobalSettings, mFalsingManager, @@ -276,4 +291,126 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { verify(mUserSwitcherController) .removeUserSwitchCallback(any(UserSwitcherController.UserSwitchCallback.class)); } + + @Test + public void onBouncerVisibilityChanged_allConditionsGood_sideFpsHintShown() { + setupConditionsToEnableSideFpsHint(); + reset(mSidefpsController); + + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); + + verify(mSidefpsController).show(); + verify(mSidefpsController, never()).hide(); + } + + @Test + public void onBouncerVisibilityChanged_fpsSensorNotRunning_sideFpsHintHidden() { + setupConditionsToEnableSideFpsHint(); + setFingerprintDetectionRunning(false); + reset(mSidefpsController); + + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); + + verify(mSidefpsController).hide(); + verify(mSidefpsController, never()).show(); + } + + @Test + public void onBouncerVisibilityChanged_withoutSidedSecurity_sideFpsHintHidden() { + setupConditionsToEnableSideFpsHint(); + setSidedSecurityMode(false); + reset(mSidefpsController); + + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); + + verify(mSidefpsController).hide(); + verify(mSidefpsController, never()).show(); + } + + @Test + public void onBouncerVisibilityChanged_needsStrongAuth_sideFpsHintHidden() { + setupConditionsToEnableSideFpsHint(); + setNeedsStrongAuth(true); + reset(mSidefpsController); + + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); + + verify(mSidefpsController).hide(); + verify(mSidefpsController, never()).show(); + } + + @Test + public void onBouncerVisibilityChanged_sideFpsHintShown_sideFpsHintHidden() { + setupGetSecurityView(); + setupConditionsToEnableSideFpsHint(); + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); + verify(mSidefpsController, atLeastOnce()).show(); + reset(mSidefpsController); + + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.INVISIBLE); + + verify(mSidefpsController).hide(); + verify(mSidefpsController, never()).show(); + } + + @Test + public void onStartingToHide_sideFpsHintShown_sideFpsHintHidden() { + setupGetSecurityView(); + setupConditionsToEnableSideFpsHint(); + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); + verify(mSidefpsController, atLeastOnce()).show(); + reset(mSidefpsController); + + mKeyguardSecurityContainerController.onStartingToHide(); + + verify(mSidefpsController).hide(); + verify(mSidefpsController, never()).show(); + } + + @Test + public void onPause_sideFpsHintShown_sideFpsHintHidden() { + setupGetSecurityView(); + setupConditionsToEnableSideFpsHint(); + mKeyguardSecurityContainerController.onBouncerVisibilityChanged(View.VISIBLE); + verify(mSidefpsController, atLeastOnce()).show(); + reset(mSidefpsController); + + mKeyguardSecurityContainerController.onPause(); + + verify(mSidefpsController).hide(); + verify(mSidefpsController, never()).show(); + } + + private void setupConditionsToEnableSideFpsHint() { + attachView(); + setSidedSecurityMode(true); + setFingerprintDetectionRunning(true); + setNeedsStrongAuth(false); + } + + private void attachView() { + mKeyguardSecurityContainerController.onViewAttached(); + verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture()); + } + + private void setFingerprintDetectionRunning(boolean running) { + when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(running); + mKeyguardUpdateMonitorCallback.getValue().onBiometricRunningStateChanged(running, + BiometricSourceType.FINGERPRINT); + } + + private void setSidedSecurityMode(boolean sided) { + when(mView.isSidedSecurityMode()).thenReturn(sided); + } + + private void setNeedsStrongAuth(boolean needed) { + when(mKeyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(needed); + mKeyguardUpdateMonitorCallback.getValue().onStrongAuthStateChanged(/* userId= */ 0); + } + + private void setupGetSecurityView() { + when(mKeyguardSecurityViewFlipperController.getSecurityView( + any(), any(KeyguardSecurityCallback.class))) + .thenReturn((KeyguardInputViewController) mKeyguardPasswordViewControllerMock); + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 2dc066c8a9db..84903d17852f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -1109,8 +1109,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // THEN face unlock is not running b/c status bar state changes don't cause biometric // listening state to update - assertThat(mKeyguardUpdateMonitor.isFaceUnlockRunning( - KeyguardUpdateMonitor.getCurrentUser())).isEqualTo(false); + assertThat(mKeyguardUpdateMonitor.isFaceDetectionRunning()).isEqualTo(false); // WHEN biometric listening state is updated mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java index 5d8e4351cfd9..a0fdc8f1555e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/DependencyTest.java @@ -26,6 +26,8 @@ import com.android.systemui.statusbar.policy.FlashlightController; import org.junit.Assert; import org.junit.Test; +import java.util.concurrent.ExecutionException; + @SmallTest public class DependencyTest extends SysuiTestCase { @@ -44,10 +46,12 @@ public class DependencyTest extends SysuiTestCase { } @Test - public void testInitDependency() { + public void testInitDependency() throws ExecutionException, InterruptedException { Dependency.clearDependencies(); - Dependency dependency = - SystemUIFactory.getInstance().getSysUIComponent().createDependency(); + SystemUIInitializer initializer = + SystemUIInitializerFactory.createFromConfigNoAssert(mContext); + initializer.init(true); + Dependency dependency = initializer.getSysUIComponent().createDependency(); dependency.start(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java index 8c20b248d02c..9179efc9f39f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiBaseFragmentTest.java @@ -34,6 +34,8 @@ import org.junit.Before; import org.junit.Rule; import org.mockito.Mockito; +import java.util.concurrent.ExecutionException; + public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { public static final Class<?>[] ALL_SUPPORTED_CLASSES = LeakCheckedTest.ALL_SUPPORTED_CLASSES; @@ -54,10 +56,11 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { } @Before - public void SysuiSetup() { - SystemUIFactory.createFromConfig(mContext, true); - mDependency = new TestableDependency( - SystemUIFactory.getInstance().getSysUIComponent().createDependency()); + public void sysuiSetup() throws ExecutionException, InterruptedException { + SystemUIInitializer initializer = + SystemUIInitializerFactory.createFromConfigNoAssert(mContext); + initializer.init(true); + mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency()); Dependency.setInstance(mDependency); // TODO: Figure out another way to give reference to a SysuiTestableContext. @@ -77,7 +80,6 @@ public abstract class SysuiBaseFragmentTest extends BaseFragmentTest { public void SysuiTeardown() { InstrumentationRegistry.registerInstance(mRealInstrumentation, InstrumentationRegistry.getArguments()); - SystemUIFactory.cleanup(); } @AfterClass diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index 8c7927782d2d..c52ea60f0bfc 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -76,9 +76,10 @@ public abstract class SysuiTestCase { @Before public void SysuiSetup() throws Exception { - SystemUIFactory.createFromConfig(mContext, true); - mDependency = new TestableDependency( - SystemUIFactory.getInstance().getSysUIComponent().createDependency()); + SystemUIInitializer initializer = + SystemUIInitializerFactory.createFromConfigNoAssert(mContext); + initializer.init(true); + mDependency = new TestableDependency(initializer.getSysUIComponent().createDependency()); Dependency.setInstance(mDependency); mFakeBroadcastDispatcher = new FakeBroadcastDispatcher(mContext, mock(Looper.class), mock(Executor.class), mock(DumpManager.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java index 7e9f84c1ef8c..bebd8712a750 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java @@ -191,6 +191,16 @@ public class AccessibilityFloatingMenuViewTest extends SysuiTestCase { } @Test + public void hideMenuViewWhenStartingAnimation_animatorNotRunning() { + mMenuView.show(); + + mMenuView.mDragAnimator.start(); + mMenuView.hide(); + + assertThat(mMenuView.mDragAnimator.isRunning()).isFalse(); + } + + @Test public void onTargetsChanged_singleTarget_expectedRadii() { final Position alignRightPosition = new Position(1.0f, 0.0f); final AccessibilityFloatingMenuView menuView = new AccessibilityFloatingMenuView(mContext, diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt index 1e2074bfab98..4218e0904c43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt @@ -170,19 +170,27 @@ class DialogLaunchAnimatorTest : SysuiTestCase() { @Test fun testCujSpecificationLogsInteraction() { val touchSurface = createTouchSurface() - return runOnMainThreadAndWaitForIdleSync { + runOnMainThreadAndWaitForIdleSync { val dialog = TestDialog(context) dialogLaunchAnimator.showFromView( - dialog, touchSurface, - cuj = DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN) - ) + dialog, touchSurface, cuj = DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN)) } - verify(interactionJankMonitor).begin( - any() - ) - verify(interactionJankMonitor) - .end(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN) + verify(interactionJankMonitor).begin(any()) + verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN) + } + + @Test + fun testShowFromDialogCujSpecificationLogsInteraction() { + val firstDialog = createAndShowDialog() + runOnMainThreadAndWaitForIdleSync { + val dialog = TestDialog(context) + dialogLaunchAnimator.showFromDialog( + dialog, firstDialog, cuj = DialogCuj(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN)) + dialog + } + verify(interactionJankMonitor).begin(any()) + verify(interactionJankMonitor).end(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN) } private fun createAndShowDialog(): TestDialog { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt index b61fbbe1ea75..273786d74782 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt @@ -207,25 +207,30 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { } @Test - fun animatesAppearingViewsFromStartToEnd() { - // Starting GONE. - rootView.visibility = View.GONE - rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */) - var success = ViewHierarchyAnimator.animateAddition(rootView) - rootView.visibility = View.VISIBLE - rootView.layout(0 /* l */, 100 /* t */, 100 /* r */, 200 /* b */) + fun animatesInvisibleViews() { + rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */) + rootView.visibility = View.INVISIBLE + + val success = ViewHierarchyAnimator.animate(rootView) + // Change all bounds. + rootView.layout(0 /* l */, 15 /* t */, 70 /* r */, 80 /* b */) assertTrue(success) assertNotNull(rootView.getTag(R.id.tag_animator)) - checkBounds(rootView, l = 50, t = 150, r = 50, b = 150) + // The initial values should be those of the previous layout. + checkBounds(rootView, l = 10, t = 10, r = 50, b = 50) endAnimation(rootView) assertNull(rootView.getTag(R.id.tag_animator)) - checkBounds(rootView, l = 0, t = 100, r = 100, b = 200) + // The end values should be those of the latest layout. + checkBounds(rootView, l = 0, t = 15, r = 70, b = 80) + } - // Starting INVISIBLE. - rootView.visibility = View.INVISIBLE + @Test + fun animatesAppearingViewsFromStartToEnd() { + // Starting GONE. + rootView.visibility = View.GONE rootView.layout(0 /* l */, 50 /* t */, 50 /* r */, 100 /* b */) - success = ViewHierarchyAnimator.animateAddition(rootView) + var success = ViewHierarchyAnimator.animateAddition(rootView) rootView.visibility = View.VISIBLE rootView.layout(0 /* l */, 100 /* t */, 100 /* r */, 200 /* b */) @@ -937,7 +942,7 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { } @Test - fun doesNotAnimateInvisibleViews() { + fun doesNotAnimateGoneViews() { rootView.layout(10 /* l */, 10 /* t */, 50 /* r */, 50 /* b */) // GONE @@ -948,15 +953,6 @@ ViewHierarchyAnimatorTest : SysuiTestCase() { assertFalse(success) assertNull(rootView.getTag(R.id.tag_animator)) checkBounds(rootView, l = 0, t = 15, r = 55, b = 80) - - // INVISIBLE. - rootView.visibility = View.INVISIBLE - success = ViewHierarchyAnimator.animate(rootView) - rootView.layout(0 /* l */, 20 /* t */, 10 /* r */, 50 /* b */) - - assertFalse(success) - assertNull(rootView.getTag(R.id.tag_animator)) - checkBounds(rootView, l = 0, t = 20, r = 10, b = 50) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index bc5a4d3d274e..bf3788e4c76a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -48,11 +48,12 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.anyInt +import org.mockito.Mockito.anyLong import org.mockito.Mockito.eq import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever +import org.mockito.junit.MockitoJUnit @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) @@ -87,7 +88,7 @@ class AuthContainerViewTest : SysuiTestCase() { @Test fun testNotifiesAnimatedIn() { initializeFingerprintContainer() - verify(callback).onDialogAnimatedIn() + verify(callback).onDialogAnimatedIn(authContainer?.requestId ?: 0L) } @Test @@ -96,13 +97,13 @@ class AuthContainerViewTest : SysuiTestCase() { container.dismissFromSystemServer() waitForIdleSync() - verify(callback, never()).onDialogAnimatedIn() + verify(callback, never()).onDialogAnimatedIn(anyLong()) container.addToView() waitForIdleSync() // attaching the view resets the state and allows this to happen again - verify(callback).onDialogAnimatedIn() + verify(callback).onDialogAnimatedIn(authContainer?.requestId ?: 0L) } @Test @@ -110,14 +111,17 @@ class AuthContainerViewTest : SysuiTestCase() { val container = initializeFingerprintContainer() waitForIdleSync() - verify(callback).onDialogAnimatedIn() + val requestID = authContainer?.requestId ?: 0L + + verify(callback).onDialogAnimatedIn(requestID) container.onWindowFocusChanged(false) waitForIdleSync() verify(callback).onDismissed( - eq(AuthDialogCallback.DISMISSED_USER_CANCELED), - eq<ByteArray?>(null) /* credentialAttestation */ + eq(AuthDialogCallback.DISMISSED_USER_CANCELED), + eq<ByteArray?>(null), /* credentialAttestation */ + eq(requestID) ) assertThat(container.parent).isNull() } @@ -131,8 +135,9 @@ class AuthContainerViewTest : SysuiTestCase() { waitForIdleSync() verify(callback).onDismissed( - eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED), - eq<ByteArray?>(null) /* credentialAttestation */ + eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED), + eq<ByteArray?>(null), /* credentialAttestation */ + eq(authContainer?.requestId ?: 0L) ) assertThat(container.parent).isNull() } @@ -146,11 +151,13 @@ class AuthContainerViewTest : SysuiTestCase() { waitForIdleSync() verify(callback).onSystemEvent( - eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL) + eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL), + eq(authContainer?.requestId ?: 0L) ) verify(callback).onDismissed( - eq(AuthDialogCallback.DISMISSED_USER_CANCELED), - eq<ByteArray?>(null) /* credentialAttestation */ + eq(AuthDialogCallback.DISMISSED_USER_CANCELED), + eq<ByteArray?>(null), /* credentialAttestation */ + eq(authContainer?.requestId ?: 0L) ) assertThat(container.parent).isNull() } @@ -164,8 +171,9 @@ class AuthContainerViewTest : SysuiTestCase() { waitForIdleSync() verify(callback).onDismissed( - eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE), - eq<ByteArray?>(null) /* credentialAttestation */ + eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE), + eq<ByteArray?>(null), /* credentialAttestation */ + eq(authContainer?.requestId ?: 0L) ) assertThat(container.parent).isNull() } @@ -180,7 +188,7 @@ class AuthContainerViewTest : SysuiTestCase() { ) waitForIdleSync() - verify(callback).onTryAgainPressed() + verify(callback).onTryAgainPressed(authContainer?.requestId ?: 0L) } @Test @@ -192,8 +200,9 @@ class AuthContainerViewTest : SysuiTestCase() { waitForIdleSync() verify(callback).onDismissed( - eq(AuthDialogCallback.DISMISSED_ERROR), - eq<ByteArray?>(null) /* credentialAttestation */ + eq(AuthDialogCallback.DISMISSED_ERROR), + eq<ByteArray?>(null), /* credentialAttestation */ + eq(authContainer?.requestId ?: 0L) ) assertThat(authContainer!!.parent).isNull() } @@ -209,7 +218,7 @@ class AuthContainerViewTest : SysuiTestCase() { ) waitForIdleSync() - verify(callback).onDeviceCredentialPressed() + verify(callback).onDeviceCredentialPressed(authContainer?.requestId ?: 0L) assertThat(container.hasCredentialView()).isTrue() } @@ -297,6 +306,16 @@ class AuthContainerViewTest : SysuiTestCase() { } @Test + fun testLayoutParams_hasDimbehindWindowFlag() { + val layoutParams = AuthContainerView.getLayoutParams(windowToken, "") + val lpFlags = layoutParams.flags + val lpDimAmount = layoutParams.dimAmount + + assertThat((lpFlags and WindowManager.LayoutParams.FLAG_DIM_BEHIND) != 0).isTrue() + assertThat(lpDimAmount).isGreaterThan(0f) + } + + @Test fun testLayoutParams_excludesImeInsets() { val layoutParams = AuthContainerView.getLayoutParams(windowToken, "") assertThat((layoutParams.fitInsetsTypes and WindowInsets.Type.ime()) == 0).isTrue() @@ -312,12 +331,12 @@ class AuthContainerViewTest : SysuiTestCase() { container.onAuthenticationFailed(BiometricAuthenticator.TYPE_FACE, "failed") waitForIdleSync() - verify(callback, never()).onTryAgainPressed() + verify(callback, never()).onTryAgainPressed(anyLong()) container.onPointerDown() waitForIdleSync() - verify(callback).onTryAgainPressed() + verify(callback).onTryAgainPressed(authContainer?.requestId ?: 0L) } private fun initializeFingerprintContainer( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index d948a99f8ad8..d158892e4ec5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -291,7 +291,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception { showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), eq(null) /* credentialAttestation */); @@ -301,7 +302,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_NEGATIVE), eq(null) /* credentialAttestation */); @@ -311,7 +313,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED), eq(null) /* credentialAttestation */); @@ -321,7 +324,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED), eq(null) /* credentialAttestation */); @@ -331,7 +335,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonError_whenDismissedByError() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_ERROR), eq(null) /* credentialAttestation */); @@ -341,7 +346,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED), eq(null) /* credentialAttestation */); @@ -355,7 +361,7 @@ public class AuthControllerTest extends SysuiTestCase { final byte[] credentialAttestation = generateRandomHAT(); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED, - credentialAttestation); + credentialAttestation, mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED), AdditionalMatchers.aryEq(credentialAttestation)); @@ -531,7 +537,7 @@ public class AuthControllerTest extends SysuiTestCase { final byte[] credentialAttestation = generateRandomHAT(); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED, - credentialAttestation); + credentialAttestation, mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED), AdditionalMatchers.aryEq(credentialAttestation)); @@ -640,17 +646,19 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); + final long requestID = mAuthController.mCurrentDialog.getRequestId(); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, - null /* credentialAttestation */); - mAuthController.onTryAgainPressed(); + null, /* credentialAttestation */requestID); + mAuthController.onTryAgainPressed(requestID); } @Test public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); + final long requestID = mAuthController.mCurrentDialog.getRequestId(); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, - null /* credentialAttestation */); - mAuthController.onDeviceCredentialPressed(); + null /* credentialAttestation */, requestID); + mAuthController.onDeviceCredentialPressed(requestID); } @Test @@ -708,7 +716,8 @@ public class AuthControllerTest extends SysuiTestCase { // WHEN dialog is shown and then dismissed showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, - null /* credentialAttestation */); + null /* credentialAttestation */, + mAuthController.mCurrentDialog.getRequestId()); // THEN callback should be received verify(callback).onBiometricPromptDismissed(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS new file mode 100644 index 000000000000..adb10f01b5e1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/OWNERS @@ -0,0 +1,4 @@ +set noparent + +include /services/core/java/com/android/server/biometrics/OWNERS +beverlyt@google.com diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt index dec2b82ed88f..6157ccbd597a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt @@ -62,7 +62,6 @@ import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.eq import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.any import org.mockito.Mockito.anyFloat import org.mockito.Mockito.anyInt @@ -72,6 +71,7 @@ import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenEver import org.mockito.junit.MockitoJUnit private const val DISPLAY_ID = 2 @@ -126,15 +126,15 @@ class SidefpsControllerTest : SysuiTestCase() { context.addMockSystemService(DisplayManager::class.java, displayManager) context.addMockSystemService(WindowManager::class.java, windowManager) - `when`(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView) - `when`(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation))) + whenEver(layoutInflater.inflate(R.layout.sidefps_view, null, false)).thenReturn(sidefpsView) + whenEver(sidefpsView.findViewById<LottieAnimationView>(eq(R.id.sidefps_animation))) .thenReturn(mock(LottieAnimationView::class.java)) with(mock(ViewPropertyAnimator::class.java)) { - `when`(sidefpsView.animate()).thenReturn(this) - `when`(alpha(anyFloat())).thenReturn(this) - `when`(setStartDelay(anyLong())).thenReturn(this) - `when`(setDuration(anyLong())).thenReturn(this) - `when`(setListener(any())).thenAnswer { + whenEver(sidefpsView.animate()).thenReturn(this) + whenEver(alpha(anyFloat())).thenReturn(this) + whenEver(setStartDelay(anyLong())).thenReturn(this) + whenEver(setDuration(anyLong())).thenReturn(this) + whenEver(setListener(any())).thenAnswer { (it.arguments[0] as Animator.AnimatorListener) .onAnimationEnd(mock(Animator::class.java)) this @@ -177,7 +177,7 @@ class SidefpsControllerTest : SysuiTestCase() { displayBounds = Rect(0, 0, displayWidth, displayHeight) var locations = listOf(sensorLocation) - `when`(fingerprintManager.sensorPropertiesInternal).thenReturn( + whenEver(fingerprintManager.sensorPropertiesInternal).thenReturn( listOf( FingerprintSensorPropertiesInternal( SENSOR_ID, @@ -196,12 +196,12 @@ class SidefpsControllerTest : SysuiTestCase() { displayInfo.initInfo() val dmGlobal = mock(DisplayManagerGlobal::class.java) val display = Display(dmGlobal, DISPLAY_ID, displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS) - `when`(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo) - `when`(windowManager.defaultDisplay).thenReturn(display) - `when`(windowManager.maximumWindowMetrics).thenReturn( + whenEver(dmGlobal.getDisplayInfo(eq(DISPLAY_ID))).thenReturn(displayInfo) + whenEver(windowManager.defaultDisplay).thenReturn(display) + whenEver(windowManager.maximumWindowMetrics).thenReturn( WindowMetrics(displayBounds, WindowInsets.CONSUMED) ) - `when`(windowManager.currentWindowMetrics).thenReturn( + whenEver(windowManager.currentWindowMetrics).thenReturn( WindowMetrics(displayBounds, windowInsets) ) @@ -277,13 +277,13 @@ class SidefpsControllerTest : SysuiTestCase() { @Test fun testShowsForMostSettings() = testWithDisplay { - `when`(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpEnrollTask())) + whenEver(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpEnrollTask())) testIgnoredFor(REASON_AUTH_SETTINGS, ignored = false) } @Test fun testIgnoredForVerySpecificSettings() = testWithDisplay { - `when`(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpSettingsTask())) + whenEver(activityTaskManager.getTasks(anyInt())).thenReturn(listOf(fpSettingsTask())) testIgnoredFor(REASON_AUTH_SETTINGS) } @@ -424,6 +424,20 @@ class SidefpsControllerTest : SysuiTestCase() { assertThat(overlayViewParamsCaptor.value.x).isEqualTo(displayWidth - boundsWidth) assertThat(overlayViewParamsCaptor.value.y).isEqualTo(sensorLocation.sensorLocationY) } + + @Test + fun hasSideFpsSensor_withSensorProps_returnsTrue() = testWithDisplay { + // By default all those tests assume the side fps sensor is available. + + assertThat(fingerprintManager.hasSideFpsSensor()).isTrue() + } + + @Test + fun hasSideFpsSensor_withoutSensorProps_returnsFalse() { + whenEver(fingerprintManager.sensorPropertiesInternal).thenReturn(null) + + assertThat(fingerprintManager.hasSideFpsSensor()).isFalse() + } } private fun insetsForSmallNavbar() = insetsWithBottom(60) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 638e6f32a241..09dc8e4fdb8e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -740,12 +740,12 @@ public class UdfpsControllerTest extends SysuiTestCase { anyString(), any(), eq("udfps-onStart-click"), - eq(UdfpsController.VIBRATION_ATTRIBUTES)); + eq(UdfpsController.UDFPS_VIBRATION_ATTRIBUTES)); // THEN make sure vibration attributes has so that it always will play the haptic, // even in battery saver mode assertEquals(VibrationAttributes.USAGE_COMMUNICATION_REQUEST, - UdfpsController.VIBRATION_ATTRIBUTES.getUsage()); + UdfpsController.UDFPS_VIBRATION_ATTRIBUTES.getUsage()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt new file mode 100644 index 000000000000..ca94ea826782 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt @@ -0,0 +1,311 @@ +/* + * 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.camera + +import android.app.ActivityManager +import android.app.IActivityTaskManager +import android.content.ComponentName +import android.content.ContentResolver +import android.content.Intent +import android.content.pm.ActivityInfo +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import androidx.test.filters.SmallTest +import com.android.systemui.ActivityIntentHelper +import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.phone.CentralSurfaces +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.mockito.KotlinArgumentCaptor +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class CameraGestureHelperTest : SysuiTestCase() { + + @Mock + lateinit var centralSurfaces: CentralSurfaces + @Mock + lateinit var keyguardStateController: KeyguardStateController + @Mock + lateinit var packageManager: PackageManager + @Mock + lateinit var activityManager: ActivityManager + @Mock + lateinit var activityStarter: ActivityStarter + @Mock + lateinit var activityIntentHelper: ActivityIntentHelper + @Mock + lateinit var activityTaskManager: IActivityTaskManager + @Mock + lateinit var cameraIntents: CameraIntentsWrapper + @Mock + lateinit var contentResolver: ContentResolver + + private lateinit var underTest: CameraGestureHelper + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(cameraIntents.getSecureCameraIntent()).thenReturn( + Intent(CameraIntents.DEFAULT_SECURE_CAMERA_INTENT_ACTION) + ) + whenever(cameraIntents.getInsecureCameraIntent()).thenReturn( + Intent(CameraIntents.DEFAULT_INSECURE_CAMERA_INTENT_ACTION) + ) + + prepare() + + underTest = CameraGestureHelper( + context = mock(), + centralSurfaces = centralSurfaces, + keyguardStateController = keyguardStateController, + packageManager = packageManager, + activityManager = activityManager, + activityStarter = activityStarter, + activityIntentHelper = activityIntentHelper, + activityTaskManager = activityTaskManager, + cameraIntents = cameraIntents, + contentResolver = contentResolver, + uiExecutor = MoreExecutors.directExecutor(), + ) + } + + /** + * Prepares for tests by setting up the various mocks to emulate a specific device state. + * + * <p>Safe to call multiple times in a single test (for example, once in [setUp] and once in the + * actual test case). + * + * @param isCameraAllowedByAdmin Whether the device administrator allows use of the camera app + * @param installedCameraAppCount The number of installed camera apps on the device + * @param isUsingSecureScreenLockOption Whether the user-controlled setting for Screen Lock is + * set with a "secure" option that requires the user to provide some secret/credentials to be + * able to unlock the device, for example "Face Unlock", "PIN", or "Password". Examples of + * non-secure options are "None" and "Swipe" + * @param isCameraActivityRunningOnTop Whether the camera activity is running at the top of the + * most recent/current task of activities + * @param isTaskListEmpty Whether there are no active activity tasks at all. Note that this is + * treated as `false` if [isCameraActivityRunningOnTop] is set to `true` + */ + private fun prepare( + isCameraAllowedByAdmin: Boolean = true, + installedCameraAppCount: Int = 1, + isUsingSecureScreenLockOption: Boolean = true, + isCameraActivityRunningOnTop: Boolean = false, + isTaskListEmpty: Boolean = false, + ) { + whenever(centralSurfaces.isCameraAllowedByAdmin).thenReturn(isCameraAllowedByAdmin) + + whenever(activityIntentHelper.wouldLaunchResolverActivity(any(), anyInt())) + .thenReturn(installedCameraAppCount > 1) + + whenever(keyguardStateController.isMethodSecure).thenReturn(isUsingSecureScreenLockOption) + whenever(keyguardStateController.canDismissLockScreen()) + .thenReturn(!isUsingSecureScreenLockOption) + + if (installedCameraAppCount >= 1) { + val resolveInfo = ResolveInfo().apply { + this.activityInfo = ActivityInfo().apply { + packageName = CAMERA_APP_PACKAGE_NAME + } + } + whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn( + resolveInfo + ) + } else { + whenever(packageManager.resolveActivityAsUser(any(), anyInt(), anyInt())).thenReturn( + null + ) + } + + when { + isCameraActivityRunningOnTop -> { + val runningTaskInfo = ActivityManager.RunningTaskInfo().apply { + topActivity = ComponentName(CAMERA_APP_PACKAGE_NAME, "cameraActivity") + } + whenever(activityManager.getRunningTasks(anyInt())).thenReturn( + listOf( + runningTaskInfo + ) + ) + } + isTaskListEmpty -> { + whenever(activityManager.getRunningTasks(anyInt())).thenReturn(emptyList()) + } + else -> { + whenever(activityManager.getRunningTasks(anyInt())).thenReturn(listOf()) + } + } + } + + @Test + fun `canCameraGestureBeLaunched - status bar state is keyguard - returns true`() { + assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue() + } + + @Test + fun `canCameraGestureBeLaunched - state is shade-locked - returns true`() { + assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue() + } + + @Test + fun `canCameraGestureBeLaunched - state is keyguard - camera activity on top - returns true`() { + prepare(isCameraActivityRunningOnTop = true) + + assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isTrue() + } + + @Test + fun `canCameraGestureBeLaunched - state is shade-locked - camera activity on top - true`() { + prepare(isCameraActivityRunningOnTop = true) + + assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE_LOCKED)).isTrue() + } + + @Test + fun `canCameraGestureBeLaunched - not allowed by admin - returns false`() { + prepare(isCameraAllowedByAdmin = false) + + assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse() + } + + @Test + fun `canCameraGestureBeLaunched - intent does not resolve to any app - returns false`() { + prepare(installedCameraAppCount = 0) + + assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.KEYGUARD)).isFalse() + } + + @Test + fun `canCameraGestureBeLaunched - state is shade - no running tasks - returns true`() { + prepare(isCameraActivityRunningOnTop = false, isTaskListEmpty = true) + + assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue() + } + + @Test + fun `canCameraGestureBeLaunched - state is shade - camera activity on top - returns false`() { + prepare(isCameraActivityRunningOnTop = true) + + assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isFalse() + } + + @Test + fun `canCameraGestureBeLaunched - state is shade - camera activity not on top - true`() { + assertThat(underTest.canCameraGestureBeLaunched(StatusBarState.SHADE)).isTrue() + } + + @Test + fun `launchCamera - only one camera app installed - using secure screen lock option`() { + val source = 1337 + + underTest.launchCamera(source) + + assertActivityStarting(isSecure = true, source = source) + } + + @Test + fun `launchCamera - only one camera app installed - using non-secure screen lock option`() { + prepare(isUsingSecureScreenLockOption = false) + val source = 1337 + + underTest.launchCamera(source) + + assertActivityStarting(isSecure = false, source = source) + } + + @Test + fun `launchCamera - multiple camera apps installed - using secure screen lock option`() { + prepare(installedCameraAppCount = 2) + val source = 1337 + + underTest.launchCamera(source) + + assertActivityStarting( + isSecure = true, + source = source, + moreThanOneCameraAppInstalled = true + ) + } + + @Test + fun `launchCamera - multiple camera apps installed - using non-secure screen lock option`() { + prepare( + isUsingSecureScreenLockOption = false, + installedCameraAppCount = 2, + ) + val source = 1337 + + underTest.launchCamera(source) + + assertActivityStarting( + isSecure = false, + moreThanOneCameraAppInstalled = true, + source = source + ) + } + + private fun assertActivityStarting( + isSecure: Boolean, + source: Int, + moreThanOneCameraAppInstalled: Boolean = false, + ) { + val intentCaptor = KotlinArgumentCaptor(Intent::class.java) + if (isSecure && !moreThanOneCameraAppInstalled) { + verify(activityTaskManager).startActivityAsUser( + any(), + any(), + any(), + intentCaptor.capture(), + any(), + any(), + any(), + anyInt(), + anyInt(), + any(), + any(), + anyInt() + ) + } else { + verify(activityStarter).startActivity(intentCaptor.capture(), eq(false)) + } + val intent = intentCaptor.value + + assertThat(CameraIntents.isSecureCameraIntent(intent)).isEqualTo(isSecure) + assertThat(intent.getIntExtra(CameraGestureHelper.EXTRA_CAMERA_LAUNCH_SOURCE, -1)) + .isEqualTo(source) + } + + companion object { + private const val CAMERA_APP_PACKAGE_NAME = "cameraAppPackageName" + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index 3c7ea4fe6f35..c31fd828c730 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -37,9 +37,14 @@ import com.android.systemui.controls.ControlsServiceInfo import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.ui.ControlsUiController import com.android.systemui.dump.DumpManager +import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import java.io.File +import java.util.Optional +import java.util.function.Consumer import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -50,20 +55,20 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers +import org.mockito.ArgumentMatchers.anyString import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.anyInt -import org.mockito.Mockito.`when` import org.mockito.Mockito.inOrder import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.reset +import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import java.util.Optional -import java.util.function.Consumer @SmallTest @RunWith(AndroidTestingRunner::class) @@ -85,6 +90,8 @@ class ControlsControllerImplTest : SysuiTestCase() { private lateinit var listingController: ControlsListingController @Mock(stubOnly = true) private lateinit var userTracker: UserTracker + @Mock + private lateinit var userFileManager: UserFileManager @Captor private lateinit var structureInfoCaptor: ArgumentCaptor<StructureInfo> @@ -153,6 +160,9 @@ class ControlsControllerImplTest : SysuiTestCase() { canceller = DidRunRunnable() `when`(bindingController.bindAndLoad(any(), any())).thenReturn(canceller) + `when`(userFileManager.getFile(anyString(), anyInt())).thenReturn(mock(File::class.java)) + `when`(userFileManager.getSharedPreferences(anyString(), anyInt(), anyInt())) + .thenReturn(context.getSharedPreferences("test", Context.MODE_PRIVATE)) controller = ControlsControllerImpl( wrapper, @@ -161,6 +171,7 @@ class ControlsControllerImplTest : SysuiTestCase() { bindingController, listingController, broadcastDispatcher, + userFileManager, Optional.of(persistenceWrapper), mock(DumpManager::class.java), userTracker @@ -217,6 +228,7 @@ class ControlsControllerImplTest : SysuiTestCase() { bindingController, listingController, broadcastDispatcher, + userFileManager, Optional.of(persistenceWrapper), mock(DumpManager::class.java), userTracker @@ -911,6 +923,14 @@ class ControlsControllerImplTest : SysuiTestCase() { assertTrue(controller.getFavoritesForStructure(TEST_COMPONENT_2, TEST_STRUCTURE).isEmpty()) } + + @Test + fun testUserStructure() { + val userStructure = UserStructure(context, context.user, userFileManager) + verify(userFileManager, times(2)) + .getFile(ControlsFavoritePersistenceWrapper.FILE_NAME, context.user.identifier) + assertThat(userStructure.file).isNotNull() + } } private class DidRunRunnable() : Runnable { diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java index e5a75e231f8d..49cdfa72f344 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeUiTest.java @@ -86,7 +86,7 @@ public class DozeUiTest extends SysuiTestCase { mHandler = mHandlerThread.getThreadHandler(); mDozeUi = new DozeUi(mContext, mAlarmManager, mWakeLock, mHost, mHandler, - mDozeParameters, mKeyguardUpdateMonitor, mStatusBarStateController, mDozeLog); + mDozeParameters, mStatusBarStateController, mDozeLog); mDozeUi.setDozeMachine(mMachine); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java index d334694805fe..60e5a9423c61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java @@ -184,25 +184,36 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { } @Test - public void testOnViewAttachedShowsMicCameraIconWhenDisabled() { + public void testOnViewAttachedShowsMicIconWhenDisabled() { when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE)) .thenReturn(true); when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) - .thenReturn(true); + .thenReturn(false); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); + DreamOverlayStatusBarView.STATUS_ICON_MIC_DISABLED, true, null); } @Test - public void testOnViewAttachedHidesMicCameraIconWhenEnabled() { + public void testOnViewAttachedShowsCameraIconWhenDisabled() { when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE)) .thenReturn(false); when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) - .thenReturn(false); + .thenReturn(true); + mController.onViewAttached(); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_CAMERA_DISABLED, true, null); + } + + @Test + public void testOnViewAttachedShowsMicCameraIconWhenDisabled() { + when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE)) + .thenReturn(true); + when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) + .thenReturn(true); mController.onViewAttached(); verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false, null); + DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); } @Test @@ -386,24 +397,6 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { } @Test - public void testMicCameraIconHiddenWhenSensorsNotBlocked() { - when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE)) - .thenReturn(true).thenReturn(false); - when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) - .thenReturn(true).thenReturn(false); - mController.onViewAttached(); - - final ArgumentCaptor<IndividualSensorPrivacyController.Callback> callbackCapture = - ArgumentCaptor.forClass(IndividualSensorPrivacyController.Callback.class); - verify(mSensorPrivacyController).addCallback(callbackCapture.capture()); - callbackCapture.getValue().onSensorBlockedChanged( - SensorPrivacyManager.Sensors.MICROPHONE, false); - - verify(mView).showIcon( - DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false, null); - } - - @Test public void testPriorityModeIconShownWhenZenModeEnabled() { mController.onViewAttached(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/AirQualityColorPickerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/AirQualityColorPickerTest.java deleted file mode 100644 index 33be5dcfe0a0..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/AirQualityColorPickerTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * 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.dreams.complication; - -import static com.google.common.truth.Truth.assertThat; - -import android.testing.AndroidTestingRunner; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.SysuiTestCase; - -import org.junit.Test; -import org.junit.runner.RunWith; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class AirQualityColorPickerTest extends SysuiTestCase { - private static final int DEFAULT_COLOR = 0; - private static final int MOCK_COLOR_1 = 1; - private static final int MOCK_COLOR_2 = 2; - private static final int MOCK_COLOR_3 = 3; - private static final int MOCK_COLOR_4 = 4; - private static final int MOCK_COLOR_5 = 5; - - private static final int[] MOCK_THRESHOLDS = {-1, 100, 200, 201, 500}; - private static final int[] MOCK_COLORS = - {MOCK_COLOR_1, MOCK_COLOR_2, MOCK_COLOR_3, MOCK_COLOR_4, MOCK_COLOR_5}; - private static final int[] EMPTY_ARRAY = {}; - - @Test - public void testEmptyThresholds() { - final AirQualityColorPicker colorPicker = new AirQualityColorPicker( - EMPTY_ARRAY, - MOCK_COLORS, - DEFAULT_COLOR); - assertThat(colorPicker.getColorForValue("110 AQI")).isEqualTo(DEFAULT_COLOR); - } - - @Test - public void testEmptyColors() { - final AirQualityColorPicker colorPicker = new AirQualityColorPicker( - MOCK_THRESHOLDS, - EMPTY_ARRAY, - DEFAULT_COLOR); - assertThat(colorPicker.getColorForValue("110 AQI")).isEqualTo(DEFAULT_COLOR); - } - - @Test - public void testEmptyAqiString() { - final AirQualityColorPicker colorPicker = new AirQualityColorPicker( - MOCK_THRESHOLDS, - MOCK_COLORS, - DEFAULT_COLOR); - assertThat(colorPicker.getColorForValue("")).isEqualTo(DEFAULT_COLOR); - } - - @Test - public void testInvalidAqiString() { - final AirQualityColorPicker colorPicker = new AirQualityColorPicker( - MOCK_THRESHOLDS, - MOCK_COLORS, - DEFAULT_COLOR); - assertThat(colorPicker.getColorForValue("invalid")).isEqualTo(DEFAULT_COLOR); - } - - @Test - public void testZeroAirQuality() { - final AirQualityColorPicker colorPicker = new AirQualityColorPicker( - MOCK_THRESHOLDS, - MOCK_COLORS, - DEFAULT_COLOR); - assertThat(colorPicker.getColorForValue("0 AQI")).isEqualTo(MOCK_COLOR_1); - } - - @Test - public void testVeryLargeAirQuality() { - final AirQualityColorPicker colorPicker = new AirQualityColorPicker( - MOCK_THRESHOLDS, - MOCK_COLORS, - DEFAULT_COLOR); - assertThat(colorPicker.getColorForValue("100000 AQI")).isEqualTo(MOCK_COLOR_5); - } - - @Test - public void testAirQuality200() { - final AirQualityColorPicker colorPicker = new AirQualityColorPicker( - MOCK_THRESHOLDS, - MOCK_COLORS, - DEFAULT_COLOR); - assertThat(colorPicker.getColorForValue("200 AQI")).isEqualTo(MOCK_COLOR_2); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamAirQualityComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamAirQualityComplicationTest.java deleted file mode 100644 index b8a7059e4bb7..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamAirQualityComplicationTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * 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.dreams.complication; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.testing.AndroidTestingRunner; -import android.widget.TextView; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.dreams.DreamOverlayStateController; -import com.android.systemui.dreams.complication.DreamAirQualityComplication.DreamAirQualityViewController; -import com.android.systemui.dreams.smartspace.DreamSmartspaceController; -import com.android.systemui.plugins.ActivityStarter; - -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) -public class DreamAirQualityComplicationTest extends SysuiTestCase { - private static final String TRAMPOLINE_COMPONENT = "TestComponent"; - - @Mock - private DreamSmartspaceController mDreamSmartspaceController; - - @Mock - private DreamOverlayStateController mDreamOverlayStateController; - - @Mock - private DreamAirQualityComplication mComplication; - - @Mock - private AirQualityColorPicker mColorPicker; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - } - - /** - * Ensures {@link DreamAirQualityComplication} is registered. - */ - @Test - public void testComplicationRegistered() { - final DreamAirQualityComplication.Registrant registrant = - new DreamAirQualityComplication.Registrant( - mContext, - mDreamOverlayStateController, - mComplication); - registrant.start(); - verify(mDreamOverlayStateController).addComplication(eq(mComplication)); - } - - @Test - public void testGetUnfilteredTargets() { - final DreamAirQualityViewController controller = - new DreamAirQualityViewController( - mock(TextView.class), - mDreamSmartspaceController, - TRAMPOLINE_COMPONENT, - mock(ActivityStarter.class), - mColorPicker); - controller.onViewAttached(); - verify(mDreamSmartspaceController).addUnfilteredListener(any()); - controller.onViewDetached(); - verify(mDreamSmartspaceController).removeUnfilteredListener(any()); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java deleted file mode 100644 index 883bec465815..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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.dreams.complication; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.content.Context; -import android.testing.AndroidTestingRunner; -import android.widget.TextView; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.SysuiTestCase; -import com.android.systemui.dreams.DreamOverlayStateController; -import com.android.systemui.dreams.smartspace.DreamSmartspaceController; -import com.android.systemui.plugins.ActivityStarter; - -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) -public class DreamWeatherComplicationTest extends SysuiTestCase { - private static final String TRAMPOLINE_COMPONENT = "TestComponent"; - - @SuppressWarnings("HidingField") - @Mock - private Context mContext; - - @Mock - private DreamSmartspaceController mDreamSmartspaceController; - - @Mock - private DreamOverlayStateController mDreamOverlayStateController; - - @Mock - private DreamWeatherComplication mComplication; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - } - - /** - * Ensures {@link DreamWeatherComplication} is registered. - */ - @Test - public void testComplicationRegistered() { - final DreamWeatherComplication.Registrant registrant = - new DreamWeatherComplication.Registrant( - mContext, - mDreamOverlayStateController, - mComplication); - registrant.start(); - verify(mDreamOverlayStateController).addComplication(eq(mComplication)); - } - - @Test - public void testGetUnfilteredTargets() { - final DreamWeatherComplication.DreamWeatherViewController controller = - new DreamWeatherComplication.DreamWeatherViewController(mock( - TextView.class), TRAMPOLINE_COMPONENT, mock(ActivityStarter.class), - mDreamSmartspaceController); - controller.onViewAttached(); - verify(mDreamSmartspaceController).addUnfilteredListener(any()); - controller.onViewDetached(); - verify(mDreamSmartspaceController).removeUnfilteredListener(any()); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java index 51c258055465..23516c94d851 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java @@ -45,6 +45,7 @@ import androidx.slice.core.SliceQuery; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.SystemUIInitializerImpl; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationMediaManager; @@ -100,7 +101,7 @@ public class KeyguardSliceProviderTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); mIsZenMode = false; mProvider = new TestableKeyguardSliceProvider(); - mProvider.setContextAvailableCallback(context -> { }); + mProvider.setContextAvailableCallback(context -> new SystemUIInitializerImpl(mContext)); mProvider.attachInfo(getContext(), null); reset(mContentResolver); SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST))); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index a80aed7a6d18..9b665555562a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -203,6 +203,13 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mViewMediator.mViewMediatorCallback.getBouncerPromptReason()); } + @Test + public void testHideSurfaceBehindKeyguardMarksKeyguardNotGoingAway() { + mViewMediator.hideSurfaceBehindKeyguard(); + + verify(mKeyguardStateController).notifyKeyguardGoingAway(false); + } + private void createAndStartViewMediator() { mViewMediator = new KeyguardViewMediator( mContext, diff --git a/packages/SystemUI/tests/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwnerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwnerTest.kt new file mode 100644 index 000000000000..4f5c570ee812 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/lifecycle/WindowAddedViewLifecycleOwnerTest.kt @@ -0,0 +1,150 @@ +package com.android.systemui.lifecycle + +import android.view.View +import android.view.ViewTreeObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleRegistry +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class WindowAddedViewLifecycleOwnerTest : SysuiTestCase() { + + @Mock lateinit var view: View + @Mock lateinit var viewTreeObserver: ViewTreeObserver + + private lateinit var underTest: WindowAddedViewLifecycleOwner + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(view.viewTreeObserver).thenReturn(viewTreeObserver) + whenever(view.isAttachedToWindow).thenReturn(false) + whenever(view.windowVisibility).thenReturn(View.INVISIBLE) + whenever(view.hasWindowFocus()).thenReturn(false) + + underTest = WindowAddedViewLifecycleOwner(view) { LifecycleRegistry.createUnsafe(it) } + } + + @Test + fun `detached - invisible - does not have focus -- INITIALIZED`() { + assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED) + } + + @Test + fun `detached - invisible - has focus -- INITIALIZED`() { + whenever(view.hasWindowFocus()).thenReturn(true) + val captor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>() + verify(viewTreeObserver).addOnWindowFocusChangeListener(capture(captor)) + captor.value.onWindowFocusChanged(true) + + assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED) + } + + @Test + fun `detached - visible - does not have focus -- INITIALIZED`() { + whenever(view.windowVisibility).thenReturn(View.VISIBLE) + val captor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>() + verify(viewTreeObserver).addOnWindowVisibilityChangeListener(capture(captor)) + captor.value.onWindowVisibilityChanged(View.VISIBLE) + + assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED) + } + + @Test + fun `detached - visible - has focus -- INITIALIZED`() { + whenever(view.hasWindowFocus()).thenReturn(true) + val focusCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>() + verify(viewTreeObserver).addOnWindowFocusChangeListener(capture(focusCaptor)) + focusCaptor.value.onWindowFocusChanged(true) + + whenever(view.windowVisibility).thenReturn(View.VISIBLE) + val visibilityCaptor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>() + verify(viewTreeObserver).addOnWindowVisibilityChangeListener(capture(visibilityCaptor)) + visibilityCaptor.value.onWindowVisibilityChanged(View.VISIBLE) + + assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED) + } + + @Test + fun `attached - invisible - does not have focus -- CREATED`() { + whenever(view.isAttachedToWindow).thenReturn(true) + val captor = argumentCaptor<ViewTreeObserver.OnWindowAttachListener>() + verify(viewTreeObserver).addOnWindowAttachListener(capture(captor)) + captor.value.onWindowAttached() + + assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED) + } + + @Test + fun `attached - invisible - has focus -- CREATED`() { + whenever(view.isAttachedToWindow).thenReturn(true) + val attachCaptor = argumentCaptor<ViewTreeObserver.OnWindowAttachListener>() + verify(viewTreeObserver).addOnWindowAttachListener(capture(attachCaptor)) + attachCaptor.value.onWindowAttached() + + whenever(view.hasWindowFocus()).thenReturn(true) + val focusCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>() + verify(viewTreeObserver).addOnWindowFocusChangeListener(capture(focusCaptor)) + focusCaptor.value.onWindowFocusChanged(true) + + assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.CREATED) + } + + @Test + fun `attached - visible - does not have focus -- STARTED`() { + whenever(view.isAttachedToWindow).thenReturn(true) + val attachCaptor = argumentCaptor<ViewTreeObserver.OnWindowAttachListener>() + verify(viewTreeObserver).addOnWindowAttachListener(capture(attachCaptor)) + attachCaptor.value.onWindowAttached() + + whenever(view.windowVisibility).thenReturn(View.VISIBLE) + val visibilityCaptor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>() + verify(viewTreeObserver).addOnWindowVisibilityChangeListener(capture(visibilityCaptor)) + visibilityCaptor.value.onWindowVisibilityChanged(View.VISIBLE) + + assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.STARTED) + } + + @Test + fun `attached - visible - has focus -- RESUMED`() { + whenever(view.isAttachedToWindow).thenReturn(true) + val attachCaptor = argumentCaptor<ViewTreeObserver.OnWindowAttachListener>() + verify(viewTreeObserver).addOnWindowAttachListener(capture(attachCaptor)) + attachCaptor.value.onWindowAttached() + + whenever(view.hasWindowFocus()).thenReturn(true) + val focusCaptor = argumentCaptor<ViewTreeObserver.OnWindowFocusChangeListener>() + verify(viewTreeObserver).addOnWindowFocusChangeListener(capture(focusCaptor)) + focusCaptor.value.onWindowFocusChanged(true) + + whenever(view.windowVisibility).thenReturn(View.VISIBLE) + val visibilityCaptor = argumentCaptor<ViewTreeObserver.OnWindowVisibilityChangeListener>() + verify(viewTreeObserver).addOnWindowVisibilityChangeListener(capture(visibilityCaptor)) + visibilityCaptor.value.onWindowVisibilityChanged(View.VISIBLE) + + assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.RESUMED) + } + + @Test + fun dispose() { + underTest.dispose() + + verify(viewTreeObserver).removeOnWindowAttachListener(any()) + verify(viewTreeObserver).removeOnWindowVisibilityChangeListener(any()) + verify(viewTreeObserver).removeOnWindowFocusChangeListener(any()) + } +} 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 c13c30baed0a..178502269e73 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -561,6 +561,19 @@ public class MediaControlPanelTest : SysuiTestCase() { } @Test + fun bindAlbumView_artUsesResource() { + val albumArt = Icon.createWithResource(context, R.drawable.ic_android) + val state = mediaData.copy(artwork = albumArt) + + player.attachPlayer(viewHolder) + player.bindPlayer(state, PACKAGE) + bgExecutor.runAllReady() + mainExecutor.runAllReady() + + verify(albumView).setImageDrawable(any(Drawable::class.java)) + } + + @Test fun bindAlbumView_setAfterExecutors() { val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888) val canvas = Canvas(bmp) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index 59475cf0cb90..568e0cb22f18 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -71,14 +71,13 @@ public class MediaOutputAdapterTest extends SysuiTestCase { @Before public void setUp() { - mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController, mMediaOutputDialog); + mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController); mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter .onCreateViewHolder(new LinearLayout(mContext), 0); mSpyMediaOutputSeekbar = spy(mViewHolder.mSeekBar); when(mMediaOutputController.getMediaDevices()).thenReturn(mMediaDevices); when(mMediaOutputController.hasAdjustVolumeUserRestriction()).thenReturn(false); - when(mMediaOutputController.isZeroMode()).thenReturn(false); when(mMediaOutputController.isTransferring()).thenReturn(false); when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat); when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat); @@ -98,28 +97,12 @@ public class MediaOutputAdapterTest extends SysuiTestCase { } @Test - public void getItemCount_nonZeroMode_isDeviceSize() { - assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size()); - } - - @Test - public void getItemCount_zeroMode_containExtraOneForPairNew() { - when(mMediaOutputController.isZeroMode()).thenReturn(true); - + public void getItemCount_containExtraOneForPairNew() { assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size() + 1); } @Test - public void getItemCount_withDynamicGroup_containExtraOneForGroup() { - when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices); - when(mMediaOutputController.isZeroMode()).thenReturn(false); - - assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaDevices.size()); - } - - @Test - public void onBindViewHolder_zeroMode_bindPairNew_verifyView() { - when(mMediaOutputController.isZeroMode()).thenReturn(true); + public void onBindViewHolder_bindPairNew_verifyView() { mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE); @@ -133,7 +116,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase { @Test public void onBindViewHolder_bindGroup_withSessionName_verifyView() { when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices); - when(mMediaOutputController.isZeroMode()).thenReturn(false); when(mMediaOutputController.getSessionName()).thenReturn(TEST_SESSION_NAME); mMediaOutputAdapter.getItemCount(); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); @@ -148,7 +130,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase { @Test public void onBindViewHolder_bindGroup_noSessionName_verifyView() { when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices); - when(mMediaOutputController.isZeroMode()).thenReturn(false); when(mMediaOutputController.getSessionName()).thenReturn(null); mMediaOutputAdapter.getItemCount(); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0); @@ -257,7 +238,6 @@ public class MediaOutputAdapterTest extends SysuiTestCase { @Test public void onItemClick_clickPairNew_verifyLaunchBluetoothPairing() { - when(mMediaOutputController.isZeroMode()).thenReturn(true); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); mViewHolder.mContainerLayout.performClick(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java index 9eaa20c2afed..d414660018a8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java @@ -43,6 +43,8 @@ import android.widget.TextView; import androidx.core.graphics.drawable.IconCompat; import androidx.test.filters.SmallTest; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; import com.android.settingslib.bluetooth.LocalBluetoothManager; import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; @@ -98,10 +100,17 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { private CharSequence mHeaderSubtitle; private String mStopText; private boolean mIsBroadcasting; + private boolean mIsBroadcastIconVisibility; + @Before public void setUp() { when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); + final CachedBluetoothDeviceManager cachedBluetoothDeviceManager = mock( + CachedBluetoothDeviceManager.class); + when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn( + cachedBluetoothDeviceManager); + when(cachedBluetoothDeviceManager.findDevice(any())).thenReturn(null); when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null); when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState); when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_NONE); @@ -153,6 +162,27 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { } @Test + public void refresh_broadcastIconVisibilityOff_broadcastIconLayoutNotVisible() { + mIsBroadcastIconVisibility = false; + + mMediaOutputBaseDialogImpl.refresh(); + final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( + R.id.broadcast_icon); + + assertThat(view.getVisibility()).isEqualTo(View.GONE); + } + @Test + public void refresh_broadcastIconVisibilityOn_broadcastIconLayoutVisible() { + mIsBroadcastIconVisibility = true; + + mMediaOutputBaseDialogImpl.refresh(); + final ImageView view = mMediaOutputBaseDialogImpl.mDialogView.requireViewById( + R.id.broadcast_icon); + + assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); + } + + @Test public void refresh_checkTitle() { mHeaderTitle = "test_string"; @@ -308,5 +338,10 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase { public CharSequence getStopButtonText() { return mStopText; } + + @Override + public int getBroadcastIconVisibility() { + return mIsBroadcastIconVisibility ? View.VISIBLE : View.GONE; + } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java index 2bf5f0fcbfcb..751c8951859c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java @@ -383,62 +383,6 @@ public class MediaOutputControllerTest extends SysuiTestCase { } @Test - public void isZeroMode_onlyFromPhoneOutput_returnTrue() { - // Multiple available devices - assertThat(mMediaOutputController.isZeroMode()).isFalse(); - when(mMediaDevice1.getDeviceType()).thenReturn( - MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); - mMediaDevices.clear(); - mMediaDevices.add(mMediaDevice1); - mMediaOutputController.start(mCb); - mMediaOutputController.onDeviceListUpdate(mMediaDevices); - - assertThat(mMediaOutputController.isZeroMode()).isTrue(); - - when(mMediaDevice1.getDeviceType()).thenReturn( - MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE); - - assertThat(mMediaOutputController.isZeroMode()).isTrue(); - - when(mMediaDevice1.getDeviceType()).thenReturn( - MediaDevice.MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE); - - assertThat(mMediaOutputController.isZeroMode()).isTrue(); - } - - @Test - public void isZeroMode_notFromPhoneOutput_returnFalse() { - when(mMediaDevice1.getDeviceType()).thenReturn( - MediaDevice.MediaDeviceType.TYPE_UNKNOWN); - mMediaDevices.clear(); - mMediaDevices.add(mMediaDevice1); - mMediaOutputController.start(mCb); - mMediaOutputController.onDeviceListUpdate(mMediaDevices); - - assertThat(mMediaOutputController.isZeroMode()).isFalse(); - - when(mMediaDevice1.getDeviceType()).thenReturn( - MediaDevice.MediaDeviceType.TYPE_FAST_PAIR_BLUETOOTH_DEVICE); - - assertThat(mMediaOutputController.isZeroMode()).isFalse(); - - when(mMediaDevice1.getDeviceType()).thenReturn( - MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE); - - assertThat(mMediaOutputController.isZeroMode()).isFalse(); - - when(mMediaDevice1.getDeviceType()).thenReturn( - MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE); - - assertThat(mMediaOutputController.isZeroMode()).isFalse(); - - when(mMediaDevice1.getDeviceType()).thenReturn( - MediaDevice.MediaDeviceType.TYPE_CAST_GROUP_DEVICE); - - assertThat(mMediaOutputController.isZeroMode()).isFalse(); - } - - @Test public void getGroupMediaDevices_differentDeviceOrder_showingSameOrder() { final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class); final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java index c45db05bacee..6afed1a846b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java @@ -172,6 +172,39 @@ public class MediaOutputDialogTest extends SysuiTestCase { } @Test + public void getBroadcastIconVisibility_isBroadcasting_returnVisible() { + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + when(mLocalBluetoothLeBroadcast.isEnabled(any())).thenReturn(true); + when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING); + when(mMediaDevice.isBLEDevice()).thenReturn(true); + + assertThat(mMediaOutputDialog.getBroadcastIconVisibility()).isEqualTo(View.VISIBLE); + } + + @Test + public void getBroadcastIconVisibility_noBroadcasting_returnGone() { + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + when(mLocalBluetoothLeBroadcast.isEnabled(any())).thenReturn(false); + when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING); + when(mMediaDevice.isBLEDevice()).thenReturn(true); + + assertThat(mMediaOutputDialog.getBroadcastIconVisibility()).isEqualTo(View.GONE); + } + + @Test + public void getBroadcastIconVisibility_remoteNonLeDevice_returnGone() { + when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( + mLocalBluetoothLeBroadcast); + when(mLocalBluetoothLeBroadcast.isEnabled(any())).thenReturn(false); + when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_PLAYING); + when(mMediaDevice.isBLEDevice()).thenReturn(false); + + assertThat(mMediaOutputDialog.getBroadcastIconVisibility()).isEqualTo(View.GONE); + } + + @Test // Check the visibility metric logging by creating a new MediaOutput dialog, // and verify if the calling times increases. public void onCreate_ShouldLogVisibility() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java deleted file mode 100644 index 9256cd32291f..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupAdapterTest.java +++ /dev/null @@ -1,222 +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.systemui.media.dialog; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.graphics.drawable.Icon; -import android.testing.AndroidTestingRunner; -import android.view.View; -import android.widget.LinearLayout; - -import androidx.core.graphics.drawable.IconCompat; -import androidx.test.filters.SmallTest; - -import com.android.settingslib.media.MediaDevice; -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.List; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -public class MediaOutputGroupAdapterTest extends SysuiTestCase { - - private static final String TEST_DEVICE_NAME_1 = "test_device_name_1"; - private static final String TEST_DEVICE_NAME_2 = "test_device_name_2"; - private static final String TEST_DEVICE_ID_1 = "test_device_id_1"; - private static final String TEST_DEVICE_ID_2 = "test_device_id_2"; - private static final int TEST_VOLUME = 10; - private static final int TEST_MAX_VOLUME = 50; - - // Mock - private MediaOutputController mMediaOutputController = mock(MediaOutputController.class); - private MediaDevice mMediaDevice1 = mock(MediaDevice.class); - private MediaDevice mMediaDevice2 = mock(MediaDevice.class); - private Icon mIcon = mock(Icon.class); - private IconCompat mIconCompat = mock(IconCompat.class); - - private MediaOutputGroupAdapter mGroupAdapter; - private MediaOutputGroupAdapter.GroupViewHolder mGroupViewHolder; - private List<MediaDevice> mGroupMediaDevices = new ArrayList<>(); - private List<MediaDevice> mSelectableMediaDevices = new ArrayList<>(); - private List<MediaDevice> mSelectedMediaDevices = new ArrayList<>(); - private List<MediaDevice> mDeselectableMediaDevices = new ArrayList<>(); - - @Before - public void setUp() { - when(mMediaOutputController.getGroupMediaDevices()).thenReturn(mGroupMediaDevices); - when(mMediaOutputController.getDeviceIconCompat(mMediaDevice1)).thenReturn(mIconCompat); - when(mMediaOutputController.getDeviceIconCompat(mMediaDevice2)).thenReturn(mIconCompat); - when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(mSelectableMediaDevices); - when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mSelectedMediaDevices); - when(mMediaOutputController.getDeselectableMediaDevice()).thenReturn( - mDeselectableMediaDevices); - when(mIconCompat.toIcon(mContext)).thenReturn(mIcon); - when(mMediaDevice1.getName()).thenReturn(TEST_DEVICE_NAME_1); - when(mMediaDevice1.getId()).thenReturn(TEST_DEVICE_ID_1); - when(mMediaDevice2.getName()).thenReturn(TEST_DEVICE_NAME_2); - when(mMediaDevice2.getId()).thenReturn(TEST_DEVICE_ID_2); - mGroupMediaDevices.add(mMediaDevice1); - mGroupMediaDevices.add(mMediaDevice2); - mSelectedMediaDevices.add(mMediaDevice1); - mSelectableMediaDevices.add(mMediaDevice2); - mDeselectableMediaDevices.add(mMediaDevice1); - - mGroupAdapter = new MediaOutputGroupAdapter(mMediaOutputController); - mGroupViewHolder = (MediaOutputGroupAdapter.GroupViewHolder) mGroupAdapter - .onCreateViewHolder(new LinearLayout(mContext), 0); - } - - @Test - public void onBindViewHolder_verifyGroupItem() { - mGroupAdapter.onBindViewHolder(mGroupViewHolder, 0); - - assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(mContext.getText( - R.string.media_output_dialog_group)); - } - - @Test - public void onBindViewHolder_singleSelectedDevice_verifyView() { - mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1); - - assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); - assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue(); - // Disabled checkBox - assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse(); - } - - @Test - public void onBindViewHolder_multipleSelectedDevice_verifyView() { - mSelectedMediaDevices.clear(); - mSelectedMediaDevices.add(mMediaDevice1); - mSelectedMediaDevices.add(mMediaDevice2); - mDeselectableMediaDevices.clear(); - mDeselectableMediaDevices.add(mMediaDevice1); - mDeselectableMediaDevices.add(mMediaDevice2); - mSelectableMediaDevices.clear(); - - mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1); - - assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_1); - assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue(); - // Enabled checkBox - assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue(); - } - - @Test - public void onBindViewHolder_notDeselectedDevice_verifyView() { - mSelectedMediaDevices.clear(); - mSelectedMediaDevices.add(mMediaDevice1); - mSelectedMediaDevices.add(mMediaDevice2); - mDeselectableMediaDevices.clear(); - mDeselectableMediaDevices.add(mMediaDevice1); - mSelectableMediaDevices.clear(); - - mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2); - - assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2); - assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mCheckBox.isChecked()).isTrue(); - // Disabled checkBox - assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isFalse(); - } - - @Test - public void onBindViewHolder_selectableDevice_verifyCheckBox() { - mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2); - - assertThat(mGroupViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE); - assertThat(mGroupViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mTwoLineTitleText.getText()).isEqualTo(TEST_DEVICE_NAME_2); - assertThat(mGroupViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE); - assertThat(mGroupViewHolder.mCheckBox.isChecked()).isFalse(); - // Enabled checkBox - assertThat(mGroupViewHolder.mCheckBox.isEnabled()).isTrue(); - } - - @Test - public void onBindViewHolder_verifyDeviceVolume() { - when(mMediaDevice1.getCurrentVolume()).thenReturn(TEST_VOLUME); - when(mMediaDevice1.getMaxVolume()).thenReturn(TEST_MAX_VOLUME); - mGroupViewHolder.mSeekBar.setVisibility(View.VISIBLE); - - mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1); - - assertThat(mGroupViewHolder.mSeekBar.getVolume()).isEqualTo(TEST_VOLUME); - } - - @Test - public void clickSelectedDevice_verifyRemoveDeviceFromPlayMedia() { - mSelectedMediaDevices.clear(); - mSelectedMediaDevices.add(mMediaDevice1); - mSelectedMediaDevices.add(mMediaDevice2); - mDeselectableMediaDevices.clear(); - mDeselectableMediaDevices.add(mMediaDevice1); - mDeselectableMediaDevices.add(mMediaDevice2); - mSelectableMediaDevices.clear(); - - mGroupAdapter.onBindViewHolder(mGroupViewHolder, 1); - mGroupViewHolder.mCheckBox.performClick(); - - verify(mMediaOutputController).removeDeviceFromPlayMedia(mMediaDevice1); - } - - @Test - public void clickSelectabelDevice_verifyAddDeviceToPlayMedia() { - mGroupAdapter.onBindViewHolder(mGroupViewHolder, 2); - - mGroupViewHolder.mCheckBox.performClick(); - - verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java deleted file mode 100644 index 4534ae6448ec..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputGroupDialogTest.java +++ /dev/null @@ -1,120 +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.systemui.media.dialog; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import android.media.AudioManager; -import android.media.session.MediaSessionManager; -import android.os.PowerExemptionManager; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.view.View; - -import androidx.test.filters.SmallTest; - -import com.android.settingslib.bluetooth.LocalBluetoothManager; -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.animation.DialogLaunchAnimator; -import com.android.systemui.broadcast.BroadcastSender; -import com.android.systemui.media.nearby.NearbyMediaDevicesManager; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.statusbar.notification.NotificationEntryManager; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper -public class MediaOutputGroupDialogTest extends SysuiTestCase { - - private static final String TEST_PACKAGE = "test_package"; - - // Mock - private MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class); - private LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class); - private ActivityStarter mStarter = mock(ActivityStarter.class); - private BroadcastSender mBroadcastSender = mock(BroadcastSender.class); - private LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class); - private MediaDevice mMediaDevice = mock(MediaDevice.class); - private MediaDevice mMediaDevice1 = mock(MediaDevice.class); - private NotificationEntryManager mNotificationEntryManager = - mock(NotificationEntryManager.class); - private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class); - private NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock( - NearbyMediaDevicesManager.class); - private final AudioManager mAudioManager = mock(AudioManager.class); - private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class); - - private MediaOutputGroupDialog mMediaOutputGroupDialog; - private MediaOutputController mMediaOutputController; - private List<MediaDevice> mMediaDevices = new ArrayList<>(); - - @Before - public void setUp() { - mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE, - mMediaSessionManager, mLocalBluetoothManager, mStarter, - mNotificationEntryManager, mDialogLaunchAnimator, - Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager); - mMediaOutputController.mLocalMediaManager = mLocalMediaManager; - mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false, mBroadcastSender, - mMediaOutputController); - mMediaOutputGroupDialog.show(); - when(mLocalMediaManager.getSelectedMediaDevice()).thenReturn(mMediaDevices); - } - - @After - public void tearDown() { - mMediaOutputGroupDialog.dismissDialog(); - } - - @Test - public void getStopButtonVisibility_returnVisible() { - assertThat(mMediaOutputGroupDialog.getStopButtonVisibility()).isEqualTo(View.VISIBLE); - } - - @Test - public void getHeaderSubtitle_singleDevice_verifyTitle() { - mMediaDevices.add(mMediaDevice); - - assertThat(mMediaOutputGroupDialog.getHeaderSubtitle()).isEqualTo( - mContext.getText(R.string.media_output_dialog_single_device)); - } - - @Test - public void getHeaderSubtitle_multipleDevices_verifyTitle() { - mMediaDevices.add(mMediaDevice); - mMediaDevices.add(mMediaDevice1); - - assertThat(mMediaOutputGroupDialog.getHeaderSubtitle()).isEqualTo(mContext.getString( - R.string.media_output_dialog_multiple_devices, mMediaDevices.size())); - } - -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index 1cfa3b2a08ff..8fa5c9324416 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -89,6 +89,7 @@ import com.android.systemui.recents.OverviewProxyService; import com.android.systemui.recents.Recents; import com.android.systemui.settings.UserContextProvider; import com.android.systemui.settings.UserTracker; +import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shared.rotation.RotationButtonController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationRemoteInputManager; @@ -97,7 +98,6 @@ import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LightBarController; import com.android.systemui.statusbar.phone.LightBarTransitionsController; -import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.policy.DeviceProvisionedController; diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java index 99f21ad4d508..c8ebd1240149 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java @@ -48,6 +48,8 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubble; @@ -106,6 +108,9 @@ public class LaunchConversationActivityTest extends SysuiTestCase { private Intent mIntent; + private FakeSystemClock mFakeSystemClock = new FakeSystemClock(); + private FakeExecutor mBgExecutor = new FakeExecutor(mFakeSystemClock); + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -114,7 +119,8 @@ public class LaunchConversationActivityTest extends SysuiTestCase { mNotifCollection, Optional.of(mBubblesManager), mUserManager, - mCommandQueue + mCommandQueue, + mBgExecutor ); verify(mCommandQueue, times(1)).addCallback(mCallbacksCaptor.capture()); @@ -193,6 +199,7 @@ public class LaunchConversationActivityTest extends SysuiTestCase { // Ensure callback removed verify(mCommandQueue).removeCallback(any()); // Clear the notification for bubbles. + FakeExecutor.exhaustExecutors(mBgExecutor); verify(mIStatusBarService, times(1)).onNotificationClear(any(), anyInt(), any(), anyInt(), anyInt(), mNotificationVisibilityCaptor.capture()); // Do not select the bubble. diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt new file mode 100644 index 000000000000..73a0cbc5054d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt @@ -0,0 +1,112 @@ +/* + * 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.net.ConnectivityManager +import android.os.Handler +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.internal.logging.MetricsLogger +import com.android.internal.logging.UiEventLogger +import com.android.internal.logging.testing.UiEventLoggerFake +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.qs.QSTile +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.qs.QSHost +import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.util.settings.GlobalSettings +import com.google.common.truth.Truth.assertThat +import dagger.Lazy +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +class AirplaneModeTileTest : SysuiTestCase() { + @Mock + private lateinit var mHost: QSHost + @Mock + private lateinit var mMetricsLogger: MetricsLogger + @Mock + private lateinit var mStatusBarStateController: StatusBarStateController + @Mock + private lateinit var mActivityStarter: ActivityStarter + @Mock + private lateinit var mQsLogger: QSLogger + @Mock + private lateinit var mBroadcastDispatcher: BroadcastDispatcher + @Mock + private lateinit var mConnectivityManager: Lazy<ConnectivityManager> + @Mock + private lateinit var mGlobalSettings: GlobalSettings + private lateinit var mTestableLooper: TestableLooper + private lateinit var mTile: AirplaneModeTile + + private val mUiEventLogger: UiEventLogger = UiEventLoggerFake() + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + mTestableLooper = TestableLooper.get(this) + Mockito.`when`(mHost.context).thenReturn(mContext) + Mockito.`when`(mHost.uiEventLogger).thenReturn(mUiEventLogger) + Mockito.`when`(mHost.userContext).thenReturn(mContext) + + mTile = AirplaneModeTile(mHost, + mTestableLooper.looper, + Handler(mTestableLooper.looper), + FalsingManagerFake(), + mMetricsLogger, + mStatusBarStateController, + mActivityStarter, + mQsLogger, + mBroadcastDispatcher, + mConnectivityManager, + mGlobalSettings) + } + + @Test + fun testIcon_whenDisabled_showsOffState() { + val state = QSTile.BooleanState() + + mTile.handleUpdateState(state, 0) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_off)) + } + + @Test + fun testIcon_whenEnabled_showsOnState() { + val state = QSTile.BooleanState() + + mTile.handleUpdateState(state, 1) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_airplane_icon_on)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt index 3d9205ee0354..95e7ad9fad4d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt @@ -24,15 +24,19 @@ import android.testing.TestableLooper.RunWithLooper import android.view.View 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.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.QSHost 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.util.settings.FakeSettings import com.android.systemui.util.settings.SecureSettings +import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -77,6 +81,7 @@ class BatterySaverTileTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) testableLooper = TestableLooper.get(this) + `when`(qsHost.context).thenReturn(mContext) `when`(qsHost.userContext).thenReturn(userContext) `when`(userContext.userId).thenReturn(USER) @@ -133,4 +138,26 @@ class BatterySaverTileTest : SysuiTestCase() { tile.handleSetListening(false) verify(batteryController).clearLastPowerSaverStartView() } + + @Test + fun testIcon_whenBatterySaverDisabled_isOffState() { + val state = QSTile.BooleanState() + tile.onPowerSaveChanged(false) + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_off)) + } + + @Test + fun testIcon_whenBatterySaverEnabled_isOnState() { + val state = QSTile.BooleanState() + tile.onPowerSaveChanged(true) + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_battery_saver_icon_on)) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt new file mode 100644 index 000000000000..cfbb82f5f338 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt @@ -0,0 +1,112 @@ +/* + * 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.os.Handler +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.internal.logging.MetricsLogger +import com.android.internal.logging.UiEventLogger +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 +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.logging.QSLogger +import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +class CameraToggleTileTest : SysuiTestCase() { + companion object { + /* isBlocked */ + const val CAMERA_TOGGLE_ENABLED: Boolean = false + const val CAMERA_TOGGLE_DISABLED: Boolean = true + } + + @Mock + private lateinit var host: QSHost + @Mock + private lateinit var metricsLogger: MetricsLogger + @Mock + private lateinit var statusBarStateController: StatusBarStateController + @Mock + private lateinit var activityStarter: ActivityStarter + @Mock + private lateinit var qsLogger: QSLogger + @Mock + private lateinit var privacyController: IndividualSensorPrivacyController + @Mock + private lateinit var keyguardStateController: KeyguardStateController + + private lateinit var testableLooper: TestableLooper + private lateinit var tile: CameraToggleTile + private val uiEventLogger: UiEventLogger = UiEventLoggerFake() + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + whenever(host.context).thenReturn(mContext) + whenever(host.uiEventLogger).thenReturn(uiEventLogger) + + tile = CameraToggleTile(host, + testableLooper.looper, + Handler(testableLooper.looper), + metricsLogger, + FalsingManagerFake(), + statusBarStateController, + activityStarter, + qsLogger, + privacyController, + keyguardStateController) + } + + @Test + fun testIcon_whenCameraAccessEnabled_isOnState() { + val state = QSTile.BooleanState() + + tile.handleUpdateState(state, CAMERA_TOGGLE_ENABLED) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_camera_access_icon_on)) + } + + @Test + fun testIcon_whenCameraAccessDisabled_isOffState() { + val state = QSTile.BooleanState() + + tile.handleUpdateState(state, CAMERA_TOGGLE_DISABLED) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_camera_access_icon_off)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt index ae70d3203d57..ce5edb147d87 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt @@ -21,6 +21,7 @@ import android.content.ContextWrapper import android.content.SharedPreferences import android.os.Handler import android.provider.Settings +import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS import android.provider.Settings.Global.ZEN_MODE_OFF import android.testing.AndroidTestingRunner import android.testing.TestableLooper @@ -28,13 +29,16 @@ import android.view.View import androidx.test.filters.SmallTest import com.android.internal.logging.MetricsLogger import com.android.internal.logging.UiEventLogger +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator 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.QSHost import com.android.systemui.qs.logging.QSLogger +import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.ZenModeController import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -66,22 +70,31 @@ class DndTileTest : SysuiTestCase() { @Mock private lateinit var qsHost: QSHost + @Mock private lateinit var metricsLogger: MetricsLogger + @Mock private lateinit var statusBarStateController: StatusBarStateController + @Mock private lateinit var activityStarter: ActivityStarter + @Mock private lateinit var qsLogger: QSLogger + @Mock private lateinit var uiEventLogger: UiEventLogger + @Mock private lateinit var zenModeController: ZenModeController + @Mock private lateinit var sharedPreferences: SharedPreferences + @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator + @Mock private lateinit var hostDialog: Dialog @@ -190,4 +203,24 @@ class DndTileTest : SysuiTestCase() { verify(dialogLaunchAnimator, never()).showFromView(any(), any(), nullable(), anyBoolean()) } + + @Test + fun testIcon_whenDndModeOff_isOffState() { + whenever(zenModeController.zen).thenReturn(ZEN_MODE_OFF) + val state = QSTile.BooleanState() + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_dnd_icon_off)) + } + + @Test + fun testIcon_whenDndModeOn_isOnState() { + whenever(zenModeController.zen).thenReturn(ZEN_MODE_NO_INTERRUPTIONS) + val state = QSTile.BooleanState() + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_dnd_icon_on)) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt new file mode 100644 index 000000000000..d2bbc8cfac39 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt @@ -0,0 +1,112 @@ +/* + * 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.Context +import android.os.Handler +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +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 +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.KeyguardStateController +import com.android.systemui.statusbar.policy.LocationController +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +class LocationTileTest : SysuiTestCase() { + + @Mock + private lateinit var mockContext: Context + @Mock + private lateinit var qsLogger: QSLogger + @Mock + private lateinit var qsHost: QSTileHost + @Mock + private lateinit var metricsLogger: MetricsLogger + private val falsingManager = FalsingManagerFake() + @Mock + private lateinit var statusBarStateController: StatusBarStateController + @Mock + private lateinit var activityStarter: ActivityStarter + @Mock + private lateinit var locationController: LocationController + @Mock + private lateinit var keyguardStateController: KeyguardStateController + + private val uiEventLogger = UiEventLoggerFake() + private lateinit var testableLooper: TestableLooper + private lateinit var tile: LocationTile + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger) + `when`(qsHost.context).thenReturn(mockContext) + + tile = LocationTile(qsHost, + testableLooper.looper, + Handler(testableLooper.looper), + falsingManager, + metricsLogger, + statusBarStateController, + activityStarter, + qsLogger, + locationController, + keyguardStateController) + } + + @Test + fun testIcon_whenDisabled_isOffState() { + val state = QSTile.BooleanState() + `when`(locationController.isLocationEnabled).thenReturn(false) + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_off)) + } + + @Test + fun testIcon_whenEnabled_isOnState() { + val state = QSTile.BooleanState() + `when`(locationController.isLocationEnabled).thenReturn(true) + + tile.handleUpdateState(state, /* arg= */ null) + + assertThat(state.icon) + .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_location_icon_on)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt new file mode 100644 index 000000000000..1ab601ce3ebe --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt @@ -0,0 +1,110 @@ +/* + * 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.os.Handler +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.internal.logging.MetricsLogger +import com.android.internal.logging.UiEventLogger +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 +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.logging.QSLogger +import com.android.systemui.qs.tileimpl.QSTileImpl +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations +import org.mockito.Mockito.`when` as whenever + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +class MicrophoneToggleTileTest : SysuiTestCase() { + companion object { + /* isBlocked */ + const val MICROPHONE_TOGGLE_ENABLED: Boolean = false + const val MICROPHONE_TOGGLE_DISABLED: Boolean = true + } + + @Mock + private lateinit var host: QSHost + @Mock + private lateinit var metricsLogger: MetricsLogger + @Mock + private lateinit var statusBarStateController: StatusBarStateController + @Mock + private lateinit var activityStarter: ActivityStarter + @Mock + private lateinit var qsLogger: QSLogger + @Mock + private lateinit var privacyController: IndividualSensorPrivacyController + @Mock + private lateinit var keyguardStateController: KeyguardStateController + + private lateinit var testableLooper: TestableLooper + private lateinit var tile: MicrophoneToggleTile + private val uiEventLogger: UiEventLogger = UiEventLoggerFake() + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + whenever(host.context).thenReturn(mContext) + whenever(host.uiEventLogger).thenReturn(uiEventLogger) + + tile = MicrophoneToggleTile(host, + testableLooper.looper, + Handler(testableLooper.looper), + metricsLogger, + FalsingManagerFake(), + statusBarStateController, + activityStarter, + qsLogger, + privacyController, + keyguardStateController) + } + + @Test + fun testIcon_whenMicrophoneAccessEnabled_isOnState() { + val state = QSTile.BooleanState() + + tile.handleUpdateState(state, MICROPHONE_TOGGLE_ENABLED) + + assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_mic_access_on)) + } + + @Test + fun testIcon_whenMicrophoneAccessDisabled_isOffState() { + val state = QSTile.BooleanState() + + tile.handleUpdateState(state, MICROPHONE_TOGGLE_DISABLED) + + assertThat(state.icon).isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_mic_access_off)) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java index 91cafead596c..b05d9a31b475 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java @@ -16,18 +16,21 @@ package com.android.systemui.screenrecord; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.Notification; import android.app.NotificationManager; import android.content.Intent; +import android.os.Handler; import android.os.RemoteException; import android.testing.AndroidTestingRunner; @@ -66,6 +69,8 @@ public class RecordingServiceTest extends SysuiTestCase { @Mock private Executor mExecutor; @Mock + private Handler mHandler; + @Mock private UserContextProvider mUserContextTracker; private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() { public void executeWhenUnlocked(ActivityStarter.OnDismissAction action, @@ -79,8 +84,8 @@ public class RecordingServiceTest extends SysuiTestCase { @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mRecordingService = Mockito.spy(new RecordingService(mController, mExecutor, mUiEventLogger, - mNotificationManager, mUserContextTracker, mKeyguardDismissUtil)); + mRecordingService = Mockito.spy(new RecordingService(mController, mExecutor, mHandler, + mUiEventLogger, mNotificationManager, mUserContextTracker, mKeyguardDismissUtil)); // Return actual context info doReturn(mContext).when(mRecordingService).getApplicationContext(); @@ -143,4 +148,54 @@ public class RecordingServiceTest extends SysuiTestCase { // Then the state is set to not recording verify(mController).updateState(false); } + + @Test + public void testOnSystemRequestedStop_recordingInProgress_endsRecording() throws IOException { + doReturn(true).when(mController).isRecording(); + + mRecordingService.onStopped(); + + verify(mScreenMediaRecorder).end(); + } + + @Test + public void testOnSystemRequestedStop_recordingInProgress_updatesState() { + doReturn(true).when(mController).isRecording(); + + mRecordingService.onStopped(); + + verify(mController).updateState(false); + } + + @Test + public void testOnSystemRequestedStop_recordingIsNotInProgress_doesNotEndRecording() + throws IOException { + doReturn(false).when(mController).isRecording(); + + mRecordingService.onStopped(); + + verify(mScreenMediaRecorder, never()).end(); + } + + @Test + public void testOnSystemRequestedStop_recorderEndThrowsRuntimeException_releasesRecording() + throws IOException { + doReturn(true).when(mController).isRecording(); + doThrow(new RuntimeException()).when(mScreenMediaRecorder).end(); + + mRecordingService.onStopped(); + + verify(mScreenMediaRecorder).release(); + } + + @Test + public void testOnSystemRequestedStop_recorderEndThrowsOOMError_releasesRecording() + throws IOException { + doReturn(true).when(mController).isRecording(); + doThrow(new OutOfMemoryError()).when(mScreenMediaRecorder).end(); + + assertThrows(Throwable.class, () -> mRecordingService.onStopped()); + + verify(mScreenMediaRecorder).release(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java index 7ab49584f0e3..e1eda117177d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ActionProxyReceiverTest.java @@ -32,7 +32,6 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.app.PendingIntent; -import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.testing.AndroidTestingRunner; @@ -115,7 +114,7 @@ public class ActionProxyReceiverTest extends SysuiTestCase { actionProxyReceiver.onReceive(mContext, mIntent); verify(mMockScreenshotSmartActions, never()) - .notifyScreenshotAction(any(Context.class), anyString(), anyString(), anyBoolean(), + .notifyScreenshotAction(anyString(), anyString(), anyBoolean(), any(Intent.class)); } @@ -129,7 +128,7 @@ public class ActionProxyReceiverTest extends SysuiTestCase { actionProxyReceiver.onReceive(mContext, mIntent); verify(mMockScreenshotSmartActions).notifyScreenshotAction( - mContext, testId, ACTION_TYPE_SHARE, false, null); + testId, ACTION_TYPE_SHARE, false, null); } private ActionProxyReceiver constructActionProxyReceiver(boolean withStatusBar) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java index 664c125533d1..d58f47a0469a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/DeleteScreenshotReceiverTest.java @@ -31,7 +31,6 @@ import static org.mockito.Mockito.verify; import android.content.ContentResolver; import android.content.ContentValues; -import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.net.Uri; @@ -81,7 +80,7 @@ public class DeleteScreenshotReceiverTest extends SysuiTestCase { verify(mMockExecutor, never()).execute(any(Runnable.class)); verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction( - any(Context.class), any(String.class), any(String.class), anyBoolean(), + any(String.class), any(String.class), anyBoolean(), any(Intent.class)); } @@ -113,7 +112,7 @@ public class DeleteScreenshotReceiverTest extends SysuiTestCase { } // ensure smart actions not called by default - verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction(any(Context.class), + verify(mMockScreenshotSmartActions, never()).notifyScreenshotAction( any(String.class), any(String.class), anyBoolean(), any(Intent.class)); } @@ -129,7 +128,7 @@ public class DeleteScreenshotReceiverTest extends SysuiTestCase { mDeleteScreenshotReceiver.onReceive(mContext, intent); verify(mMockExecutor).execute(any(Runnable.class)); - verify(mMockScreenshotSmartActions).notifyScreenshotAction(mContext, testId, + verify(mMockScreenshotSmartActions).notifyScreenshotAction(testId, ACTION_TYPE_DELETE, false, null); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java index 3d658ec8e811..69b7b88b0524 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java @@ -43,7 +43,6 @@ import android.testing.AndroidTestingRunner; import androidx.test.filters.SmallTest; -import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition; @@ -71,7 +70,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { public void setup() { mSmartActionsProvider = mock( ScreenshotNotificationSmartActionsProvider.class); - mScreenshotSmartActions = new ScreenshotSmartActions(); + mScreenshotSmartActions = new ScreenshotSmartActions(() -> mSmartActionsProvider); mHandler = mock(Handler.class); } @@ -158,8 +157,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { Bitmap bitmap = mock(Bitmap.class); when(bitmap.getConfig()).thenReturn(Bitmap.Config.HARDWARE); ScreenshotNotificationSmartActionsProvider actionsProvider = - SystemUIFactory.getInstance().createScreenshotNotificationSmartActionsProvider( - mContext, null, mHandler); + new ScreenshotNotificationSmartActionsProvider(); CompletableFuture<List<Notification.Action>> smartActionsFuture = mScreenshotSmartActions.getSmartActionsFuture("", null, bitmap, actionsProvider, REGULAR_SMART_ACTIONS, @@ -183,7 +181,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, - ActionTransition::new); + ActionTransition::new, mSmartActionsProvider); Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(), Uri.parse("Screenshot_123.png")).get().action; @@ -211,7 +209,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, - ActionTransition::new); + ActionTransition::new, mSmartActionsProvider); Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(), Uri.parse("Screenshot_123.png")).get().action; @@ -239,7 +237,7 @@ public class ScreenshotNotificationSmartActionsTest extends SysuiTestCase { data.mActionsReadyListener = null; SaveImageInBackgroundTask task = new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data, - ActionTransition::new); + ActionTransition::new, mSmartActionsProvider); Notification.Action deleteAction = task.createDeleteAction(mContext, mContext.getResources(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java index 011e6b7b1071..83c949793447 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/SmartActionsReceiverTest.java @@ -74,6 +74,6 @@ public class SmartActionsReceiverTest extends SysuiTestCase { verify(mMockPendingIntent).send( eq(mContext), eq(0), isNull(), isNull(), isNull(), isNull(), any(Bundle.class)); verify(mMockScreenshotSmartActions).notifyScreenshotAction( - mContext, testId, testActionType, true, intent); + testId, testActionType, true, intent); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt new file mode 100644 index 000000000000..73226fa0b12c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt @@ -0,0 +1,120 @@ +/* + * 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.settings + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.IntentFilter +import android.os.Environment +import android.os.UserManager +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import java.util.concurrent.Executor +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.isNull +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class UserFileManagerImplTest : SysuiTestCase() { + companion object { + const val TEST_FILE_NAME = "abc.txt" + } + + lateinit var userFileManager: UserFileManagerImpl + lateinit var backgroundExecutor: FakeExecutor + @Mock + lateinit var userManager: UserManager + @Mock + lateinit var broadcastDispatcher: BroadcastDispatcher + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + backgroundExecutor = FakeExecutor(FakeSystemClock()) + userFileManager = UserFileManagerImpl(context, userManager, + broadcastDispatcher, backgroundExecutor) + } + + @Test + fun testGetFile() { + assertThat(userFileManager.getFile(TEST_FILE_NAME, 0).path) + .isEqualTo("${context.filesDir}/$TEST_FILE_NAME") + assertThat(userFileManager.getFile(TEST_FILE_NAME, 11).path) + .isEqualTo("${context.filesDir}/${UserFileManagerImpl.ID}/11/files/$TEST_FILE_NAME") + } + + @Test + fun testGetSharedPreferences() { + assertThat(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 0)) + .isNotEqualTo(userFileManager.getSharedPreferences(TEST_FILE_NAME, 0, 11)) + } + + @Test + fun testUserFileManagerStart() { + val userFileManager = spy(userFileManager) + userFileManager.start() + verify(userFileManager).clearDeletedUserData() + verify(broadcastDispatcher).registerReceiver(any(BroadcastReceiver::class.java), + any(IntentFilter::class.java), + any(Executor::class.java), isNull(), eq(Context.RECEIVER_EXPORTED), isNull()) + } + + @Test + fun testClearDeletedUserData() { + val dir = Environment.buildPath( + context.filesDir, + UserFileManagerImpl.ID, + "11", + "files" + ) + dir.mkdirs() + val file = Environment.buildPath( + context.filesDir, + UserFileManagerImpl.ID, + "11", + "files", + TEST_FILE_NAME + ) + val secondaryUserDir = Environment.buildPath( + context.filesDir, + UserFileManagerImpl.ID, + "11", + ) + file.createNewFile() + assertThat(secondaryUserDir.exists()).isTrue() + assertThat(file.exists()).isTrue() + userFileManager.clearDeletedUserData() + assertThat(backgroundExecutor.runAllReady()).isGreaterThan(0) + verify(userManager).aliveUsers + assertThat(secondaryUserDir.exists()).isFalse() + assertThat(file.exists()).isFalse() + dir.deleteRecursively() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 686718a96dd2..5abcff3c56f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.shade; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; @@ -106,6 +106,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.qrcodescanner.controller.QRCodeScannerController; import com.android.systemui.screenrecord.RecordingController; +import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -131,8 +132,28 @@ import com.android.systemui.statusbar.notification.stack.NotificationRoundnessMa import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; +import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; +import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; +import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; +import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController; +import com.android.systemui.statusbar.phone.LargeScreenShadeHeaderController; +import com.android.systemui.statusbar.phone.LockscreenGestureLogger; +import com.android.systemui.statusbar.phone.NotificationIconAreaController; +import com.android.systemui.statusbar.phone.PanelViewController; +import com.android.systemui.statusbar.phone.ScreenOffAnimationController; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; +import com.android.systemui.statusbar.phone.TapAgainViewController; +import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; -import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -173,6 +194,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { @Mock private KeyguardBottomAreaView mKeyguardBottomArea; @Mock + private KeyguardBottomAreaViewController mKeyguardBottomAreaViewController; + @Mock private KeyguardBottomAreaView mQsFrame; private KeyguardStatusView mKeyguardStatusView; @Mock @@ -400,6 +423,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000); when(mNotificationStackScrollLayoutController.getHeadsUpCallback()) .thenReturn(mHeadsUpCallback); + when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea); when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class)); when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); @@ -482,8 +506,8 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mMainHandler = new Handler(Looper.getMainLooper()); mPanelEventsEmitter = new NotificationPanelViewController.PanelEventsEmitter(); - mNotificationPanelViewController = new NotificationPanelViewController(mView, - mResources, + mNotificationPanelViewController = new NotificationPanelViewController( + mView, mMainHandler, mLayoutInflater, mFeatureFlags, @@ -524,8 +548,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mQuickAccessWalletController, mQrCodeScannerController, mRecordingController, - mExecutor, - mSecureSettings, mLargeScreenShadeHeaderController, mScreenOffAnimationController, mLockscreenGestureLogger, @@ -536,6 +558,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mInteractionJankMonitor, mQsFrameTranslateController, mSysUiState, + () -> mKeyguardBottomAreaViewController, mKeyguardUnlockAnimationController, mNotificationListContainer, mPanelEventsEmitter, @@ -561,7 +584,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(View.AccessibilityDelegate.class); verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); mAccessibiltyDelegate = accessibilityDelegateArgumentCaptor.getValue(); - mNotificationPanelViewController.mStatusBarStateController + mNotificationPanelViewController.getStatusBarStateController() .addCallback(mNotificationPanelViewController.mStatusBarStateListener); mNotificationPanelViewController .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class)); @@ -989,6 +1012,21 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test + public void testSwipe_exactlyToTarget_notifiesNssl() { + // No over-expansion + mNotificationPanelViewController.setOverExpansion(0f); + // Fling to a target that is equal to the current position (i.e. a no-op fling). + mNotificationPanelViewController.flingToHeight( + 0f, + true, + mNotificationPanelViewController.getExpandedHeight(), + 1f, + false); + // Verify that the NSSL is notified that the panel is *not* flinging. + verify(mNotificationStackScrollLayoutController).setPanelFlinging(false); + } + + @Test public void testDoubleTapRequired_Keyguard() { FalsingManager.FalsingTapListener listener = getFalsingTapListener(); mStatusBarStateController.setState(KEYGUARD); @@ -1175,14 +1213,14 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { when(mPowerManager.isPowerSaveMode()).thenReturn(false); when(mAmbientState.getDozeAmount()).thenReturn(0f); mNotificationPanelViewController.startUnlockHintAnimation(); - assertThat(mNotificationPanelViewController.mHintAnimationRunning).isTrue(); + assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isTrue(); } @Test public void testUnlockHintAnimation_doesNotRun_inPowerSaveMode() { when(mPowerManager.isPowerSaveMode()).thenReturn(true); mNotificationPanelViewController.startUnlockHintAnimation(); - assertThat(mNotificationPanelViewController.mHintAnimationRunning).isFalse(); + assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isFalse(); } @Test @@ -1190,7 +1228,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { when(mPowerManager.isPowerSaveMode()).thenReturn(false); when(mAmbientState.getDozeAmount()).thenReturn(0.5f); mNotificationPanelViewController.startUnlockHintAnimation(); - assertThat(mNotificationPanelViewController.mHintAnimationRunning).isFalse(); + assertThat(mNotificationPanelViewController.isHintAnimationRunning()).isFalse(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt index de40b7fd0a13..0c6a6a98052f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt @@ -1,4 +1,4 @@ -package com.android.systemui.statusbar.phone +package com.android.systemui.shade import android.testing.AndroidTestingRunner import android.testing.TestableLooper diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java index c402d2e47cf3..1dfd7c26ecbb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowControllerImplTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.shade; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; @@ -28,8 +28,10 @@ import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; @@ -42,6 +44,8 @@ import android.testing.TestableLooper.RunWithLooper; import android.view.View; import android.view.WindowManager; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.ViewTreeLifecycleOwner; import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor; @@ -51,6 +55,11 @@ import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; +import com.android.systemui.statusbar.phone.ScreenOffAnimationController; +import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -61,6 +70,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.Spy; @RunWith(AndroidTestingRunner.class) @RunWithLooper @@ -69,7 +79,8 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { @Mock private WindowManager mWindowManager; @Mock private DozeParameters mDozeParameters; - @Mock private NotificationShadeWindowView mNotificationShadeWindowView; + @Spy private final NotificationShadeWindowView mNotificationShadeWindowView = spy( + new NotificationShadeWindowView(mContext, null)); @Mock private IActivityManager mActivityManager; @Mock private SysuiStatusBarStateController mStatusBarStateController; @Mock private ConfigurationController mConfigurationController; @@ -85,6 +96,7 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { private NotificationShadeWindowControllerImpl mNotificationShadeWindowController; + @Before public void setUp() { MockitoAnnotations.initMocks(this); @@ -177,6 +189,24 @@ public class NotificationShadeWindowControllerImplTest extends SysuiTestCase { } @Test + public void attach_setsUpLifecycleOwner() { + mNotificationShadeWindowController.attach(); + + assertThat(ViewTreeLifecycleOwner.get(mNotificationShadeWindowView)).isNotNull(); + } + + @Test + public void attach_doesNotSetUpLifecycleOwnerIfAlreadySet() { + final LifecycleOwner previouslySet = mock(LifecycleOwner.class); + ViewTreeLifecycleOwner.set(mNotificationShadeWindowView, previouslySet); + + mNotificationShadeWindowController.attach(); + + assertThat(ViewTreeLifecycleOwner.get(mNotificationShadeWindowView)) + .isEqualTo(previouslySet); + } + + @Test public void setScrimsVisibility_earlyReturn() { clearInvocations(mWindowManager); mNotificationShadeWindowController.setScrimsVisibility(ScrimController.TRANSPARENT); diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt new file mode 100644 index 000000000000..471918cfa695 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -0,0 +1,284 @@ +/* + * 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.shade + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.view.MotionEvent +import androidx.test.filters.SmallTest +import com.android.keyguard.LockIconViewController +import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollectorFake +import com.android.systemui.dock.DockManager +import com.android.systemui.keyguard.KeyguardUnlockAnimationController +import com.android.systemui.lowlightclock.LowLightClockController +import com.android.systemui.statusbar.LockscreenShadeTransitionController +import com.android.systemui.statusbar.NotificationShadeDepthController +import com.android.systemui.statusbar.NotificationShadeWindowController +import com.android.systemui.statusbar.SysuiStatusBarStateController +import com.android.systemui.statusbar.notification.stack.AmbientState +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler +import com.android.systemui.statusbar.phone.CentralSurfaces +import com.android.systemui.statusbar.phone.PhoneStatusBarViewController +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager +import com.android.systemui.statusbar.window.StatusBarWindowStateController +import com.android.systemui.tuner.TunerService +import com.google.common.truth.Truth.assertThat +import java.util.Optional +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers +import org.mockito.Mock +import org.mockito.Mockito.anyFloat +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +@SmallTest +class NotificationShadeWindowViewControllerTest : SysuiTestCase() { + @Mock + private lateinit var view: NotificationShadeWindowView + @Mock + private lateinit var tunserService: TunerService + @Mock + private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController + @Mock + private lateinit var centralSurfaces: CentralSurfaces + @Mock + private lateinit var dockManager: DockManager + @Mock + private lateinit var notificationPanelViewController: NotificationPanelViewController + @Mock + private lateinit var notificationShadeDepthController: NotificationShadeDepthController + @Mock + private lateinit var notificationShadeWindowController: NotificationShadeWindowController + @Mock + private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController + @Mock + private lateinit var ambientState: AmbientState + @Mock + private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController + @Mock + private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager + @Mock + private lateinit var statusBarWindowStateController: StatusBarWindowStateController + @Mock + private lateinit var lockscreenShadeTransitionController: LockscreenShadeTransitionController + @Mock + private lateinit var lockIconViewController: LockIconViewController + @Mock + private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController + @Mock + private lateinit var lowLightClockController: LowLightClockController + + private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> + private lateinit var interactionEventHandler: InteractionEventHandler + + private lateinit var underTest: NotificationShadeWindowViewController + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + whenever(view.bottom).thenReturn(VIEW_BOTTOM) + + underTest = NotificationShadeWindowViewController( + lockscreenShadeTransitionController, + FalsingCollectorFake(), + tunserService, + sysuiStatusBarStateController, + dockManager, + notificationShadeDepthController, + view, + notificationPanelViewController, + PanelExpansionStateManager(), + stackScrollLayoutController, + statusBarKeyguardViewManager, + statusBarWindowStateController, + lockIconViewController, + Optional.of(lowLightClockController), + centralSurfaces, + notificationShadeWindowController, + keyguardUnlockAnimationController, + ambientState + ) + underTest.setupExpandedStatusBar() + + interactionEventHandlerCaptor = + ArgumentCaptor.forClass(InteractionEventHandler::class.java) + verify(view).setInteractionEventHandler(interactionEventHandlerCaptor.capture()) + interactionEventHandler = interactionEventHandlerCaptor.value + } + + // Note: So far, these tests only cover interactions with the status bar view controller. More + // tests need to be added to test the rest of handleDispatchTouchEvent. + + @Test + fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() { + underTest.setStatusBarViewController(null) + + val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv) + + assertThat(returnVal).isFalse() + } + + @Test + fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() { + underTest.setStatusBarViewController(phoneStatusBarViewController) + val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0) + whenever(phoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true) + + val returnVal = interactionEventHandler.handleDispatchTouchEvent(ev) + + verify(phoneStatusBarViewController).sendTouchToView(ev) + assertThat(returnVal).isTrue() + } + + @Test + fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() { + underTest.setStatusBarViewController(phoneStatusBarViewController) + val downEvBelow = MotionEvent.obtain( + 0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0 + ) + interactionEventHandler.handleDispatchTouchEvent(downEvBelow) + + val nextEvent = MotionEvent.obtain( + 0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0 + ) + whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true) + + val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent) + + verify(phoneStatusBarViewController).sendTouchToView(nextEvent) + assertThat(returnVal).isTrue() + } + + @Test + fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() { + underTest.setStatusBarViewController(phoneStatusBarViewController) + whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) + whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true) + whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) + .thenReturn(true) + whenever(phoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true) + + val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv) + + verify(phoneStatusBarViewController).sendTouchToView(downEv) + assertThat(returnVal).isTrue() + } + + @Test + fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() { + underTest.setStatusBarViewController(phoneStatusBarViewController) + whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) + whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) + .thenReturn(true) + // Item we're testing + whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(false) + + val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv) + + verify(phoneStatusBarViewController, never()).sendTouchToView(downEv) + assertThat(returnVal).isNull() + } + + @Test + fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() { + underTest.setStatusBarViewController(phoneStatusBarViewController) + whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) + whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true) + // Item we're testing + whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) + .thenReturn(false) + + val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv) + + verify(phoneStatusBarViewController, never()).sendTouchToView(downEv) + assertThat(returnVal).isNull() + } + + @Test + fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() { + underTest.setStatusBarViewController(phoneStatusBarViewController) + whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true) + whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) + .thenReturn(true) + // Item we're testing + whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(false) + + val returnVal = interactionEventHandler.handleDispatchTouchEvent(downEv) + + verify(phoneStatusBarViewController, never()).sendTouchToView(downEv) + assertThat(returnVal).isTrue() + } + + @Test + fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() { + underTest.setStatusBarViewController(phoneStatusBarViewController) + whenever(statusBarWindowStateController.windowIsShowing()).thenReturn(true) + whenever(notificationPanelViewController.isFullyCollapsed).thenReturn(true) + whenever(phoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) + .thenReturn(true) + + // Down event first + interactionEventHandler.handleDispatchTouchEvent(downEv) + + // Then another event + val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) + whenever(phoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true) + + val returnVal = interactionEventHandler.handleDispatchTouchEvent(nextEvent) + + verify(phoneStatusBarViewController).sendTouchToView(nextEvent) + assertThat(returnVal).isTrue() + } + + @Test + fun testLowLightClockAttachedWhenExpandedStatusBarSetup() { + verify(lowLightClockController).attachLowLightClockView(ArgumentMatchers.any()) + } + + @Test + fun testLowLightClockShownWhenDozing() { + underTest.setDozing(true) + verify(lowLightClockController).showLowLightClock(true) + } + + @Test + fun testLowLightClockDozeTimeTickCalled() { + underTest.dozeTimeTick() + verify(lowLightClockController).dozeTimeTick() + } + + @Test + fun testLowLightClockHiddenWhenNotDozing() { + underTest.setDozing(true) + verify(lowLightClockController).showLowLightClock(true) + underTest.setDozing(false) + verify(lowLightClockController).showLowLightClock(false) + } +} + +private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) +private const val VIEW_BOTTOM = 100 diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java index 1d86fb13a1f6..665d849e379e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.statusbar.phone; +package com.android.systemui.shade; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -47,6 +47,9 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.stack.AmbientState; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.ShadeController; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager; import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.tuner.TunerService; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ScrimShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt index 304a274576b7..baaa447d53a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ScrimShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt @@ -1,4 +1,4 @@ -package com.android.systemui.statusbar.phone.shade.transition +package com.android.systemui.shade.transition import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt index 8b7e04bbab1b..b6f8326394fa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/ShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt @@ -1,4 +1,4 @@ -package com.android.systemui.statusbar.phone.shade.transition +package com.android.systemui.shade.transition import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest @@ -6,8 +6,10 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.qs.QS +import com.android.systemui.shade.NotificationPanelViewController +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController -import com.android.systemui.statusbar.phone.NotificationPanelViewController import com.android.systemui.statusbar.phone.panelstate.PanelExpansionChangeEvent import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager import com.android.systemui.statusbar.phone.panelstate.STATE_OPENING @@ -19,6 +21,7 @@ import org.mockito.Mock import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions +import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -32,6 +35,7 @@ class ShadeTransitionControllerTest : SysuiTestCase() { @Mock private lateinit var splitShadeOverScroller: SplitShadeOverScroller @Mock private lateinit var scrimShadeTransitionController: ScrimShadeTransitionController @Mock private lateinit var dumpManager: DumpManager + @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController private lateinit var controller: ShadeTransitionController @@ -50,7 +54,9 @@ class ShadeTransitionControllerTest : SysuiTestCase() { context, splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller }, noOpOverScroller, - scrimShadeTransitionController) + scrimShadeTransitionController, + statusBarStateController, + ) // Resetting as they are notified upon initialization. reset(noOpOverScroller, splitShadeOverScroller) @@ -80,6 +86,45 @@ class ShadeTransitionControllerTest : SysuiTestCase() { } @Test + fun onPanelStateChanged_inSplitShade_onKeyguard_forwardsToNoOpOverScroller() { + initLateProperties() + enableSplitShade() + setOnKeyguard() + + startPanelExpansion() + + verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING) + verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT) + verifyZeroInteractions(splitShadeOverScroller) + } + + @Test + fun onPanelStateChanged_inSplitShade_onLockedShade_forwardsToNoOpOverScroller() { + initLateProperties() + enableSplitShade() + setOnLockedShade() + + startPanelExpansion() + + verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING) + verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT) + verifyZeroInteractions(splitShadeOverScroller) + } + + @Test + fun onPanelExpansionChanged_inSplitShade_onUnlockedShade_forwardsToSplitShadeOverScroller() { + initLateProperties() + enableSplitShade() + setOnUnlockedShade() + + startPanelExpansion() + + verify(splitShadeOverScroller).onPanelStateChanged(STATE_OPENING) + verify(splitShadeOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT) + verifyZeroInteractions(noOpOverScroller) + } + + @Test fun onPanelStateChanged_notInSplitShade_forwardsToNoOpOverScroller() { initLateProperties() disableSplitShade() @@ -129,6 +174,23 @@ class ShadeTransitionControllerTest : SysuiTestCase() { ) } + private fun setOnKeyguard() { + setShadeState(StatusBarState.KEYGUARD) + } + + private fun setOnLockedShade() { + setShadeState(StatusBarState.SHADE_LOCKED) + } + + private fun setOnUnlockedShade() { + setShadeState(StatusBarState.SHADE) + } + + private fun setShadeState(state: Int) { + whenever(statusBarStateController.state).thenReturn(state) + whenever(statusBarStateController.currentOrUpcomingState).thenReturn(state) + } + companion object { private const val DEFAULT_DRAG_DOWN_AMOUNT = 123f private val DEFAULT_EXPANSION_EVENT = diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt index 5ca15bb93d31..aafd871f72f3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/shade/transition/SplitShadeOverScrollerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt @@ -1,4 +1,4 @@ -package com.android.systemui.statusbar.phone.shade.transition +package com.android.systemui.shade.transition import android.testing.AndroidTestingRunner import android.testing.TestableLooper diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt new file mode 100644 index 000000000000..aaa2357a1c52 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -0,0 +1,209 @@ +/* + * 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.clocks + +import org.mockito.Mockito.`when` as whenever +import android.content.Context +import android.content.ContentResolver +import android.graphics.drawable.Drawable +import android.os.Handler +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.PluginListener +import com.android.systemui.shared.plugins.PluginManager +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.eq +import junit.framework.Assert.assertEquals +import junit.framework.Assert.fail +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class ClockRegistryTest : SysuiTestCase() { + + @JvmField @Rule val mockito = MockitoJUnit.rule() + @Mock private lateinit var mockContext: Context + @Mock private lateinit var mockPluginManager: PluginManager + @Mock private lateinit var mockClock: Clock + @Mock private lateinit var mockThumbnail: Drawable + @Mock private lateinit var mockHandler: Handler + @Mock private lateinit var mockContentResolver: ContentResolver + private lateinit var pluginListener: PluginListener<ClockProviderPlugin> + private lateinit var registry: ClockRegistry + + private var settingValue: String = "" + + companion object { + private fun failFactory(): Clock { + fail("Unexpected call to createClock") + return null!! + } + + private fun failThumbnail(): Drawable? { + fail("Unexpected call to getThumbnail") + return null + } + } + + private class FakeClockPlugin : ClockProviderPlugin { + private val metadata = mutableListOf<ClockMetadata>() + private val createCallbacks = mutableMapOf<ClockId, () -> Clock>() + private val thumbnailCallbacks = mutableMapOf<ClockId, () -> Drawable?>() + + override fun getClocks() = metadata + override fun createClock(id: ClockId): Clock = createCallbacks[id]!!() + override fun getClockThumbnail(id: ClockId): Drawable? = thumbnailCallbacks[id]!!() + + fun addClock( + id: ClockId, + name: String, + create: () -> Clock = ::failFactory, + getThumbnail: () -> Drawable? = ::failThumbnail + ) { + metadata.add(ClockMetadata(id, name)) + createCallbacks[id] = create + thumbnailCallbacks[id] = getThumbnail + } + } + + @Before + fun setUp() { + whenever(mockContext.contentResolver).thenReturn(mockContentResolver) + + val captor = argumentCaptor<PluginListener<ClockProviderPlugin>>() + registry = object : ClockRegistry(mockContext, mockPluginManager, mockHandler) { + override var currentClockId: ClockId + get() = settingValue + set(value) { settingValue = value } + } + verify(mockPluginManager).addPluginListener(captor.capture(), + eq(ClockProviderPlugin::class.java)) + pluginListener = captor.value + } + + @Test + fun pluginRegistration_CorrectState() { + val plugin1 = FakeClockPlugin() + plugin1.addClock("clock_1", "clock 1") + plugin1.addClock("clock_2", "clock 2") + + val plugin2 = FakeClockPlugin() + plugin2.addClock("clock_3", "clock 3") + plugin2.addClock("clock_4", "clock 4") + + pluginListener.onPluginConnected(plugin1, mockContext) + pluginListener.onPluginConnected(plugin2, mockContext) + val list = registry.getClocks() + assertEquals(list, listOf( + ClockMetadata("clock_1", "clock 1"), + ClockMetadata("clock_2", "clock 2"), + ClockMetadata("clock_3", "clock 3"), + ClockMetadata("clock_4", "clock 4") + )) + } + + @Test + fun clockIdConflict_ErrorWithoutCrash() { + val plugin1 = FakeClockPlugin() + plugin1.addClock("clock_1", "clock 1", { mockClock }, { mockThumbnail }) + plugin1.addClock("clock_2", "clock 2", { mockClock }, { mockThumbnail }) + + val plugin2 = FakeClockPlugin() + plugin2.addClock("clock_1", "clock 1") + plugin2.addClock("clock_2", "clock 2") + + pluginListener.onPluginConnected(plugin1, mockContext) + pluginListener.onPluginConnected(plugin2, mockContext) + val list = registry.getClocks() + assertEquals(list, listOf( + ClockMetadata("clock_1", "clock 1"), + ClockMetadata("clock_2", "clock 2") + )) + + assertEquals(registry.createExampleClock("clock_1"), mockClock) + assertEquals(registry.createExampleClock("clock_2"), mockClock) + assertEquals(registry.getClockThumbnail("clock_1"), mockThumbnail) + assertEquals(registry.getClockThumbnail("clock_2"), mockThumbnail) + } + + @Test + fun createCurrentClock_pluginConnected() { + val plugin1 = FakeClockPlugin() + plugin1.addClock("clock_1", "clock 1") + plugin1.addClock("clock_2", "clock 2") + + settingValue = "clock_3" + val plugin2 = FakeClockPlugin() + plugin2.addClock("clock_3", "clock 3", { mockClock }) + plugin2.addClock("clock_4", "clock 4") + + pluginListener.onPluginConnected(plugin1, mockContext) + pluginListener.onPluginConnected(plugin2, mockContext) + + val clock = registry.createCurrentClock() + assertEquals(clock, mockClock) + } + + @Test + fun createDefaultClock_pluginDisconnected() { + val plugin1 = FakeClockPlugin() + plugin1.addClock(DEFAULT_CLOCK_ID, "default", { mockClock }) + plugin1.addClock("clock_2", "clock 2") + + settingValue = "clock_3" + val plugin2 = FakeClockPlugin() + plugin2.addClock("clock_3", "clock 3") + plugin2.addClock("clock_4", "clock 4") + + pluginListener.onPluginConnected(plugin1, mockContext) + pluginListener.onPluginConnected(plugin2, mockContext) + pluginListener.onPluginDisconnected(plugin2) + + val clock = registry.createCurrentClock() + assertEquals(clock, mockClock) + } + + @Test + fun pluginRemoved_clockChanged() { + val plugin1 = FakeClockPlugin() + plugin1.addClock("clock_1", "clock 1") + plugin1.addClock("clock_2", "clock 2") + + settingValue = "clock_3" + val plugin2 = FakeClockPlugin() + plugin2.addClock("clock_3", "clock 3", { mockClock }) + plugin2.addClock("clock_4", "clock 4") + + pluginListener.onPluginConnected(plugin1, mockContext) + pluginListener.onPluginConnected(plugin2, mockContext) + + var changeCallCount = 0 + registry.registerClockChangeListener({ changeCallCount++ }) + + pluginListener.onPluginDisconnected(plugin1) + assertEquals(0, changeCallCount) + + pluginListener.onPluginDisconnected(plugin2) + assertEquals(1, changeCallCount) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index d67e26f138f2..9c25462b7c0d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -30,6 +30,7 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED; +import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; import static com.google.common.truth.Truth.assertThat; @@ -212,7 +213,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { R.string.do_financed_disclosure_with_name, ORGANIZATION_NAME); when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); - when(mScreenLifecycle.getScreenState()).thenReturn(ScreenLifecycle.SCREEN_ON); + when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_ON); when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true); when(mIndicationArea.findViewById(R.id.keyguard_indication_text_bottom)) @@ -954,64 +955,170 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase { } @Test - public void nonBypassFaceSuccess_touchExplorationEnabled_showsSwipeToOpen() { - // GIVEN non bypass face auth and touch exploration is enabled - when(mKeyguardBypassController.canBypass()).thenReturn(false); - when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true); + public void coEx_faceSuccess_showsPressToOpen() { + // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, no a11y enabled when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser())) + .thenReturn(true); + when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); + when(mAccessibilityManager.isEnabled()).thenReturn(false); + when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); + createController(); + mController.setVisible(true); + + // WHEN face auth succeeds + when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true); + mController.getKeyguardCallback().onBiometricAuthenticated(0, + BiometricSourceType.FACE, false); + + // THEN 'face unlocked. press unlock icon to open' message shows + String pressToOpen = mContext.getString(R.string.keyguard_face_successful_unlock_press); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressToOpen); + + assertThat(mTextView.getText()).isNotEqualTo(pressToOpen); + } + + + @Test + public void coEx_faceSuccess_touchExplorationEnabled_showsFaceUnlockedSwipeToOpen() { + // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y enabled + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser())) + .thenReturn(true); + when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); + when(mAccessibilityManager.isEnabled()).thenReturn(true); + when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true); createController(); - String swipeToOpen = mContext.getString(R.string.keyguard_unlock); mController.setVisible(true); // WHEN face authenticated + when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true); mController.getKeyguardCallback().onBiometricAuthenticated(0, BiometricSourceType.FACE, false); - // THEN show 'swipe up to open' message - verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen); + // THEN show 'face unlocked. swipe up to open' message + String faceUnlockedSwipeToOpen = + mContext.getString(R.string.keyguard_face_successful_unlock_swipe); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen); } @Test - public void nonBypassFaceSuccess_a11yEnabled_showsSwipeToOpen() { - // GIVEN non bypass face auth and a11y is enabled - when(mKeyguardBypassController.canBypass()).thenReturn(false); + public void coEx_faceSuccess_a11yEnabled_showsFaceUnlockedSwipeToOpen() { + // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y is enabled + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser())) + .thenReturn(true); + when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); when(mAccessibilityManager.isEnabled()).thenReturn(true); + createController(); + mController.setVisible(true); + + // WHEN face auth is successful + when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true); + mController.getKeyguardCallback().onBiometricAuthenticated(0, + BiometricSourceType.FACE, false); + + // THEN show 'swipe up to open' message + String faceUnlockedSwipeToOpen = + mContext.getString(R.string.keyguard_face_successful_unlock_swipe); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen); + } + + @Test + public void faceOnly_faceSuccess_showsFaceUnlockedSwipeToOpen() { + // GIVEN bouncer isn't showing, can skip bouncer, no udfps supported when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser())) + .thenReturn(true); + when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); createController(); - String swipeToOpen = mContext.getString(R.string.keyguard_unlock); mController.setVisible(true); // WHEN face auth is successful + when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true); mController.getKeyguardCallback().onBiometricAuthenticated(0, BiometricSourceType.FACE, false); // THEN show 'swipe up to open' message + String faceUnlockedSwipeToOpen = + mContext.getString(R.string.keyguard_face_successful_unlock_swipe); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, faceUnlockedSwipeToOpen); + } + + @Test + public void udfpsOnly_a11yEnabled_showsSwipeToOpen() { + // GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y is enabled + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser())) + .thenReturn(true); + when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); + when(mAccessibilityManager.isEnabled()).thenReturn(true); + when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true); + createController(); + mController.setVisible(true); + + // WHEN showActionToUnlock + mController.showActionToUnlock(); + + // THEN show 'swipe up to open' message + String swipeToOpen = mContext.getString(R.string.keyguard_unlock); verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen); } @Test - public void coEx_nonBypassFaceSuccess_showsPressLockIcon() { - // GIVEN udfps is supported, non-bypass face auth, and no a11y enabled + public void udfpsOnly_showsPressToOpen() { + // GIVEN bouncer isn't showing, udfps is supported, a11y is NOT enabled, can skip bouncer + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser())) + .thenReturn(true); when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(true); - when(mKeyguardBypassController.canBypass()).thenReturn(false); - when(mKeyguardUpdateMonitor.getIsFaceAuthenticated()).thenReturn(true); when(mAccessibilityManager.isEnabled()).thenReturn(false); when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); + createController(); + mController.setVisible(true); + + // WHEN showActionToUnlock + mController.showActionToUnlock(); + + // THEN show 'press unlock icon to open' message + String pressToOpen = mContext.getString(R.string.keyguard_unlock_press); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressToOpen); + } + + @Test + public void canSkipBouncer_noSecurity_showSwipeToUnlockHint() { + // GIVEN bouncer isn't showing, can skip bouncer, no security (udfps isn't supported, + // face wasn't authenticated) when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser())) .thenReturn(true); + when(mKeyguardUpdateMonitor.isUdfpsSupported()).thenReturn(false); createController(); mController.setVisible(true); - // WHEN face auth succeeds - mController.getKeyguardCallback().onBiometricAuthenticated(0, - BiometricSourceType.FACE, false); + // WHEN showActionToUnlock + mController.showActionToUnlock(); - // THEN press unlock icon to open message shows - String pressLockIcon = mContext.getString(R.string.keyguard_face_successful_unlock_press); - verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, pressLockIcon); + // THEN show 'swipe up to open' message + String swipeToOpen = mContext.getString(R.string.keyguard_unlock); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen); + } - assertThat(mTextView.getText()).isNotEqualTo(pressLockIcon); + @Test + public void cannotSkipBouncer_showSwipeToUnlockHint() { + // GIVEN bouncer isn't showing and cannot skip bouncer + when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(false); + when(mKeyguardUpdateMonitor.getUserCanSkipBouncer(KeyguardUpdateMonitor.getCurrentUser())) + .thenReturn(false); + createController(); + mController.setVisible(true); + + // WHEN showActionToUnlock + mController.showActionToUnlock(); + + // THEN show 'swipe up to open' message + String swipeToOpen = mContext.getString(R.string.keyguard_unlock); + verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen); } private void sendUpdateDisclosureBroadcast() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index 562c97017862..fe1cd978ef91 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -1,6 +1,5 @@ package com.android.systemui.statusbar -import org.mockito.Mockito.`when` as whenever import android.test.suitebuilder.annotation.SmallTest import android.testing.AndroidTestingRunner import android.testing.TestableLooper @@ -14,6 +13,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle import com.android.systemui.media.MediaHierarchyManager import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.qs.QS +import com.android.systemui.shade.NotificationPanelViewController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.stack.AmbientState @@ -22,7 +22,6 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll import com.android.systemui.statusbar.phone.CentralSurfaces import com.android.systemui.statusbar.phone.KeyguardBypassController import com.android.systemui.statusbar.phone.LSShadeTransitionLogger -import com.android.systemui.statusbar.phone.NotificationPanelViewController import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.statusbar.policy.FakeConfigurationController import org.junit.After @@ -45,6 +44,7 @@ import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions +import org.mockito.Mockito.`when` as whenever import org.mockito.junit.MockitoJUnit private fun <T> anyObject(): T { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java index 2691ff98f4ee..34d13c76399a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationRemoteInputManagerTest.java @@ -16,12 +16,9 @@ package com.android.systemui.statusbar; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Notification; @@ -30,19 +27,14 @@ import android.os.Handler; import android.os.Looper; import android.os.SystemClock; import android.os.UserHandle; -import android.service.notification.NotificationListenerService; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import androidx.annotation.NonNull; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.NotificationRemoteInputManager.LegacyRemoteInputLifetimeExtender.RemoteInputActiveExtender; -import com.android.systemui.statusbar.NotificationRemoteInputManager.LegacyRemoteInputLifetimeExtender.RemoteInputHistoryExtender; -import com.android.systemui.statusbar.NotificationRemoteInputManager.LegacyRemoteInputLifetimeExtender.SmartReplyHistoryExtender; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -52,8 +44,6 @@ import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.policy.RemoteInputUriController; -import com.google.android.collect.Sets; - import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -76,23 +66,15 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { @Mock private NotificationRemoteInputManager.Callback mCallback; @Mock private RemoteInputController mController; @Mock private SmartReplyController mSmartReplyController; - @Mock private NotificationListenerService.RankingMap mRanking; @Mock private ExpandableNotificationRow mRow; @Mock private StatusBarStateController mStateController; @Mock private RemoteInputUriController mRemoteInputUriController; @Mock private NotificationClickNotifier mClickNotifier; - - // Dependency mocks: @Mock private NotificationEntryManager mEntryManager; @Mock private NotificationLockscreenUserManager mLockscreenUserManager; private TestableNotificationRemoteInputManager mRemoteInputManager; private NotificationEntry mEntry; - private RemoteInputHistoryExtender mRemoteInputHistoryExtender; - private SmartReplyHistoryExtender mSmartReplyHistoryExtender; - private RemoteInputActiveExtender mRemoteInputActiveExtender; - private TestableNotificationRemoteInputManager.FakeLegacyRemoteInputLifetimeExtender - mLegacyRemoteInputLifetimeExtender; @Before public void setUp() { @@ -121,21 +103,7 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { .build(); mEntry.setRow(mRow); - mRemoteInputManager.setUpWithPresenterForTest(mCallback, - mDelegate, mController); - for (NotificationLifetimeExtender extender : mRemoteInputManager.getLifetimeExtenders()) { - extender.setCallback( - mock(NotificationLifetimeExtender.NotificationSafeToRemoveCallback.class)); - } - } - - @Test - public void testPerformOnRemoveNotification() { - when(mController.isRemoteInputActive(mEntry)).thenReturn(true); - mRemoteInputManager.onPerformRemoveNotification(mEntry, mEntry.getKey()); - - assertFalse(mEntry.mRemoteEditImeVisible); - verify(mController).removeRemoteInput(mEntry, null); + mRemoteInputManager.setUpWithPresenterForTest(mCallback, mDelegate, mController); } @Test @@ -143,7 +111,6 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { when(mController.isRemoteInputActive(mEntry)).thenReturn(true); assertTrue(mRemoteInputManager.isRemoteInputActive(mEntry)); - assertTrue(mRemoteInputActiveExtender.shouldExtendLifetime(mEntry)); } @Test @@ -152,7 +119,6 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { when(mController.isSpinning(mEntry.getKey())).thenReturn(true); assertTrue(mRemoteInputManager.shouldKeepForRemoteInputHistory(mEntry)); - assertTrue(mRemoteInputHistoryExtender.shouldExtendLifetime(mEntry)); } @Test @@ -161,7 +127,6 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { mEntry.lastRemoteInputSent = SystemClock.elapsedRealtime(); assertTrue(mRemoteInputManager.shouldKeepForRemoteInputHistory(mEntry)); - assertTrue(mRemoteInputHistoryExtender.shouldExtendLifetime(mEntry)); } @Test @@ -170,20 +135,6 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { when(mSmartReplyController.isSendingSmartReply(mEntry.getKey())).thenReturn(true); assertTrue(mRemoteInputManager.shouldKeepForSmartReplyHistory(mEntry)); - assertTrue(mSmartReplyHistoryExtender.shouldExtendLifetime(mEntry)); - } - - @Test - public void testNotificationWithRemoteInputActiveIsRemovedOnCollapse() { - mRemoteInputActiveExtender.setShouldManageLifetime(mEntry, true /* shouldManage */); - - assertEquals(mLegacyRemoteInputLifetimeExtender.getEntriesKeptForRemoteInputActive(), - Sets.newArraySet(mEntry)); - - mRemoteInputManager.onPanelCollapsed(); - - assertTrue( - mLegacyRemoteInputLifetimeExtender.getEntriesKeptForRemoteInputActive().isEmpty()); } private class TestableNotificationRemoteInputManager extends NotificationRemoteInputManager { @@ -227,28 +178,5 @@ public class NotificationRemoteInputManagerTest extends SysuiTestCase { mRemoteInputController = controller; } - @NonNull - @Override - protected LegacyRemoteInputLifetimeExtender createLegacyRemoteInputLifetimeExtender( - Handler mainHandler, - NotificationEntryManager notificationEntryManager, - SmartReplyController smartReplyController) { - mLegacyRemoteInputLifetimeExtender = new FakeLegacyRemoteInputLifetimeExtender(); - return mLegacyRemoteInputLifetimeExtender; - } - - class FakeLegacyRemoteInputLifetimeExtender extends LegacyRemoteInputLifetimeExtender { - - @Override - protected void addLifetimeExtenders() { - mRemoteInputActiveExtender = new RemoteInputActiveExtender(); - mRemoteInputHistoryExtender = new RemoteInputHistoryExtender(); - mSmartReplyHistoryExtender = new SmartReplyHistoryExtender(); - mLifetimeExtenders.add(mRemoteInputHistoryExtender); - mLifetimeExtenders.add(mSmartReplyHistoryExtender); - mLifetimeExtenders.add(mRemoteInputActiveExtender); - } - } - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java index 3500e4d5f701..c75aa81e4c43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SmartReplyControllerTest.java @@ -17,15 +17,15 @@ package com.android.systemui.statusbar; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.Notification; import android.os.Handler; -import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; import android.service.notification.StatusBarNotification; @@ -39,14 +39,12 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; -import com.android.systemui.statusbar.notification.NotificationEntryManager; +import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; +import com.android.systemui.statusbar.notification.collection.coordinator.RemoteInputCoordinator; +import com.android.systemui.statusbar.notification.collection.notifcollection.InternalNotifUpdater; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; -import com.android.systemui.statusbar.phone.CentralSurfaces; -import com.android.systemui.statusbar.policy.RemoteInputUriController; import org.junit.Before; import org.junit.Test; @@ -54,8 +52,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.Optional; - @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @SmallTest @@ -67,57 +63,50 @@ public class SmartReplyControllerTest extends SysuiTestCase { private static final int TEST_CHOICE_COUNT = 4; private static final int TEST_ACTION_COUNT = 3; - private Notification mNotification; private NotificationEntry mEntry; private SmartReplyController mSmartReplyController; - private NotificationRemoteInputManager mRemoteInputManager; @Mock private NotificationVisibilityProvider mVisibilityProvider; - @Mock private RemoteInputController.Delegate mDelegate; - @Mock private NotificationRemoteInputManager.Callback mCallback; @Mock private StatusBarNotification mSbn; - @Mock private NotificationEntryManager mNotificationEntryManager; @Mock private IStatusBarService mIStatusBarService; - @Mock private StatusBarStateController mStatusBarStateController; - @Mock private RemoteInputUriController mRemoteInputUriController; @Mock private NotificationClickNotifier mClickNotifier; + @Mock private InternalNotifUpdater mInternalNotifUpdater; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mDependency.injectTestDependency(NotificationEntryManager.class, - mNotificationEntryManager); mSmartReplyController = new SmartReplyController( mock(DumpManager.class), mVisibilityProvider, mIStatusBarService, mClickNotifier); - mDependency.injectTestDependency(SmartReplyController.class, + RemoteInputCoordinator remoteInputCoordinator = new RemoteInputCoordinator( + mock(DumpManager.class), + new RemoteInputNotificationRebuilder(mContext), + mock(NotificationRemoteInputManager.class), + mock(Handler.class), mSmartReplyController); + remoteInputCoordinator.setRemoteInputController(mock(RemoteInputController.class)); + NotifPipeline notifPipeline = mock(NotifPipeline.class); + when(notifPipeline.getInternalNotifUpdater(anyString())).thenReturn(mInternalNotifUpdater); + remoteInputCoordinator.attach(notifPipeline); - mRemoteInputManager = new NotificationRemoteInputManager(mContext, - mock(NotifPipelineFlags.class), - mock(NotificationLockscreenUserManager.class), - mSmartReplyController, - mVisibilityProvider, - mNotificationEntryManager, - new RemoteInputNotificationRebuilder(mContext), - () -> Optional.of(mock(CentralSurfaces.class)), - mStatusBarStateController, - Handler.createAsync(Looper.myLooper()), - mRemoteInputUriController, - mClickNotifier, - mock(ActionClickLogger.class), - mock(DumpManager.class)); - mRemoteInputManager.setUpWithCallback(mCallback, mDelegate); - mNotification = new Notification.Builder(mContext, "") + Notification notification = new Notification.Builder(mContext, "") .setSmallIcon(R.drawable.ic_person) .setContentTitle("Title") .setContentText("Text").build(); - - mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, - 0, mNotification, new UserHandle(ActivityManager.getCurrentUser()), null, 0); + mSbn = new StatusBarNotification( + TEST_PACKAGE_NAME, + TEST_PACKAGE_NAME, + 0, + null, + TEST_UID, + 0, + notification, + new UserHandle(ActivityManager.getCurrentUser()), + null, + 0); mEntry = new NotificationEntryBuilder() .setSbn(mSbn) .build(); @@ -128,10 +117,9 @@ public class SmartReplyControllerTest extends SysuiTestCase { mSmartReplyController.smartReplySent(mEntry, TEST_CHOICE_INDEX, TEST_CHOICE_TEXT, MetricsEvent.LOCATION_UNKNOWN, false /* modifiedBeforeSending */); - // Sending smart reply should make calls to NotificationEntryManager - // to update the notification with reply and spinner. - verify(mNotificationEntryManager).updateNotification( - argThat(sbn -> sbn.getKey().equals(mSbn.getKey())), isNull()); + // Sending smart reply should update the notification with reply and spinner. + verify(mInternalNotifUpdater).onInternalNotificationUpdate( + argThat(sbn -> sbn.getKey().equals(mSbn.getKey())), anyString()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index 16b0376ba1f9..aeef6b04108e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -19,6 +19,8 @@ package com.android.systemui.statusbar.notification; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED; import static android.service.notification.NotificationListenerService.REASON_CANCEL; +import static android.service.notification.NotificationStats.DISMISSAL_SHADE; +import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL; import static com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON; @@ -41,6 +43,7 @@ import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.app.ActivityManager; @@ -50,6 +53,7 @@ import android.app.PendingIntent; import android.content.Intent; import android.graphics.drawable.Icon; import android.os.Handler; +import android.os.RemoteException; import android.os.UserHandle; import android.service.notification.NotificationListenerService.Ranking; import android.service.notification.NotificationListenerService.RankingMap; @@ -91,7 +95,9 @@ import com.android.systemui.statusbar.notification.row.NotificationEntryManagerI import com.android.systemui.statusbar.notification.row.RowInflaterTask; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.leak.LeakDetector; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -138,9 +144,14 @@ public class NotificationEntryManagerTest extends SysuiTestCase { @Mock private NotificationMediaManager mNotificationMediaManager; @Mock private NotificationRowBinder mNotificationRowBinder; @Mock private NotificationListener mNotificationListener; + @Mock private IStatusBarService mStatusBarService; + + private FakeSystemClock mFakeSystemClock = new FakeSystemClock(); + private FakeExecutor mBgExecutor = new FakeExecutor(mFakeSystemClock); private int mId; private NotificationEntry mEntry; + private DismissedByUserStats mStats; private StatusBarNotification mSbn; private NotificationEntryManager mEntryManager; @@ -191,6 +202,7 @@ public class NotificationEntryManagerTest extends SysuiTestCase { Handler.createAsync(TestableLooper.get(this).getLooper())); mEntry = createNotification(); + mStats = defaultStats(mEntry); mSbn = mEntry.getSbn(); when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false); @@ -201,9 +213,10 @@ public class NotificationEntryManagerTest extends SysuiTestCase { () -> mNotificationRowBinder, () -> mRemoteInputManager, mLeakDetector, - mock(IStatusBarService.class), + mStatusBarService, NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(), - mock(DumpManager.class) + mock(DumpManager.class), + mBgExecutor ); mEntryManager.initialize( mNotificationListener, @@ -316,6 +329,31 @@ public class NotificationEntryManagerTest extends SysuiTestCase { } @Test + public void testPerformRemoveNotification_sendRemovalToServer() throws RemoteException { + // GIVEN an entry manager with a notification + mEntryManager.addActiveNotificationForTest(mEntry); + + // GIVEN interceptor that doesn't intercept + when(mRemoveInterceptor.onNotificationRemoveRequested( + eq(mEntry.getKey()), argThat(matchEntryOnKey()), anyInt())) + .thenReturn(false); + + // WHEN the notification entry is removed + mEntryManager.performRemoveNotification(mSbn, mStats, REASON_CANCEL); + + // THEN notification removal is sent to the server + FakeExecutor.exhaustExecutors(mBgExecutor); + verify(mStatusBarService).onNotificationClear( + mSbn.getPackageName(), + mSbn.getUser().getIdentifier(), + mSbn.getKey(), + mStats.dismissalSurface, + mStats.dismissalSentiment, + mStats.notificationVisibility); + verifyNoMoreInteractions(mStatusBarService); + } + + @Test public void testRemoveNotification_onEntryRemoveNotFiredIfEntryDoesntExist() { mEntryManager.removeNotification("not_a_real_key", mRankingMap, UNDEFINED_DISMISS_REASON); @@ -573,23 +611,6 @@ public class NotificationEntryManagerTest extends SysuiTestCase { any(NotificationVisibility.class), anyBoolean(), eq(UNDEFINED_DISMISS_REASON)); } - private NotificationEntry createNotification() { - Notification.Builder n = new Notification.Builder(mContext, "id") - .setSmallIcon(R.drawable.ic_person) - .setContentTitle("Title") - .setContentText("Text"); - - return new NotificationEntryBuilder() - .setPkg(TEST_PACKAGE_NAME) - .setOpPkg(TEST_PACKAGE_NAME) - .setUid(TEST_UID) - .setId(mId++) - .setNotification(n.build()) - .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT)) - .setUser(new UserHandle(ActivityManager.getCurrentUser())) - .build(); - } - /* Tests annexed from NotificationDataTest go here */ @Test @@ -713,4 +734,28 @@ public class NotificationEntryManagerTest extends SysuiTestCase { return mManagedNotifs.contains(notificationKey); } } + + private NotificationEntry createNotification() { + Notification.Builder n = new Notification.Builder(mContext, "id") + .setSmallIcon(R.drawable.ic_person) + .setContentTitle("Title") + .setContentText("Text"); + + return new NotificationEntryBuilder() + .setPkg(TEST_PACKAGE_NAME) + .setOpPkg(TEST_PACKAGE_NAME) + .setUid(TEST_UID) + .setId(mId++) + .setNotification(n.build()) + .setChannel(new NotificationChannel("id", "", IMPORTANCE_DEFAULT)) + .setUser(new UserHandle(ActivityManager.getCurrentUser())) + .build(); + } + + private static DismissedByUserStats defaultStats(NotificationEntry entry) { + return new DismissedByUserStats( + DISMISSAL_SHADE, + DISMISS_SENTIMENT_NEUTRAL, + NotificationVisibility.obtain(entry.getKey(), 7, 2, true)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt index 9f82a5673c6e..f4458bbdc497 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt @@ -6,11 +6,11 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.SysuiTestCase +import com.android.systemui.shade.NotificationShadeWindowViewController import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper import com.android.systemui.statusbar.notification.stack.NotificationListContainer import com.android.systemui.statusbar.phone.HeadsUpManagerPhone -import com.android.systemui.statusbar.phone.NotificationShadeWindowViewController import com.android.systemui.statusbar.policy.HeadsUpUtil import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -19,8 +19,8 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit @SmallTest diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java index af43826091a0..4df99be9efd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java @@ -92,6 +92,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor; import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender; +import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; @@ -146,14 +147,13 @@ public class NotifCollectionTest extends SysuiTestCase { private NoManSimulator mNoMan; private FakeSystemClock mClock = new FakeSystemClock(); + private FakeExecutor mBgExecutor = new FakeExecutor(mClock); @Before public void setUp() { MockitoAnnotations.initMocks(this); allowTestableLooperAsMainThread(); - when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(true); - when(mEulogizer.record(any(Exception.class))).thenAnswer(i -> i.getArguments()[0]); mListenerInOrder = inOrder(mCollectionListener); @@ -164,6 +164,7 @@ public class NotifCollectionTest extends SysuiTestCase { mNotifPipelineFlags, mLogger, mMainHandler, + mBgExecutor, mEulogizer, mock(DumpManager.class)); mCollection.attach(mGroupCoalescer); @@ -463,6 +464,8 @@ public class NotifCollectionTest extends SysuiTestCase { DismissedByUserStats stats = defaultStats(entry2); mCollection.dismissNotification(entry2, defaultStats(entry2)); + FakeExecutor.exhaustExecutors(mBgExecutor); + // THEN we send the dismissal to system server verify(mStatusBarService).onNotificationClear( notif2.sbn.getPackageName(), @@ -676,6 +679,8 @@ public class NotifCollectionTest extends SysuiTestCase { mInterceptor1.onEndInterceptionCallback.onEndDismissInterception(mInterceptor1, entry, stats); + FakeExecutor.exhaustExecutors(mBgExecutor); + // THEN we send the dismissal to system server verify(mStatusBarService).onNotificationClear( eq(notif.sbn.getPackageName()), @@ -1213,6 +1218,7 @@ public class NotifCollectionTest extends SysuiTestCase { new Pair<>(entry2, defaultStats(entry2)))); // THEN we send the dismissals to system server + FakeExecutor.exhaustExecutors(mBgExecutor); verify(mStatusBarService).onNotificationClear( notif1.sbn.getPackageName(), notif1.sbn.getUser().getIdentifier(), @@ -1579,6 +1585,7 @@ public class NotifCollectionTest extends SysuiTestCase { // WHEN finally dismissing onDismiss.run(); + FakeExecutor.exhaustExecutors(mBgExecutor); verify(mStatusBarService).onNotificationClear(any(), anyInt(), eq(notifEvent.key), anyInt(), anyInt(), any()); verifyNoMoreInteractions(mStatusBarService); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java index 555adfdfdc31..dfa38abc1ff8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java @@ -1028,37 +1028,37 @@ public class ShadeListBuilderTest extends SysuiTestCase { // WHEN each pluggable is invalidated THEN the list is re-rendered clearInvocations(mOnRenderListListener); - packageFilter.invalidateList(); + packageFilter.invalidateList(null); assertTrue(mPipelineChoreographer.isScheduled()); mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); - idPromoter.invalidateList(); + idPromoter.invalidateList(null); assertTrue(mPipelineChoreographer.isScheduled()); mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); - section.invalidateList(); + section.invalidateList(null); assertTrue(mPipelineChoreographer.isScheduled()); mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); - hypeComparator.invalidateList(); + hypeComparator.invalidateList(null); assertTrue(mPipelineChoreographer.isScheduled()); mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); - sectionComparator.invalidateList(); + sectionComparator.invalidateList(null); assertTrue(mPipelineChoreographer.isScheduled()); mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); clearInvocations(mOnRenderListListener); - preRenderInvalidator.invalidateList(); + preRenderInvalidator.invalidateList(null); assertTrue(mPipelineChoreographer.isScheduled()); mPipelineChoreographer.runIfScheduled(); verify(mOnRenderListListener).onRenderList(anyList()); @@ -1584,7 +1584,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { // WHEN visual stability manager allows group changes again mStabilityManager.setAllowGroupChanges(true); - mStabilityManager.invalidateList(); + mStabilityManager.invalidateList(null); mPipelineChoreographer.runIfScheduled(); // THEN entries are grouped @@ -1623,7 +1623,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { // WHEN section changes are allowed again mStabilityManager.setAllowSectionChanges(true); - mStabilityManager.invalidateList(); + mStabilityManager.invalidateList(null); mPipelineChoreographer.runIfScheduled(); // THEN the section updates @@ -1719,7 +1719,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { public void testOutOfOrderPreGroupFilterInvalidationThrows() { // GIVEN a PreGroupNotifFilter that gets invalidated during the grouping stage NotifFilter filter = new PackageFilter(PACKAGE_5); - OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList(); + OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList(null); mListBuilder.addPreGroupFilter(filter); mListBuilder.addOnBeforeTransformGroupsListener(listener); @@ -1735,7 +1735,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a NotifPromoter that gets invalidated during the sorting stage NotifPromoter promoter = new IdPromoter(47); OnBeforeSortListener listener = - (list) -> promoter.invalidateList(); + (list) -> promoter.invalidateList(null); mListBuilder.addPromoter(promoter); mListBuilder.addOnBeforeSortListener(listener); @@ -1751,7 +1751,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { // GIVEN a NotifComparator that gets invalidated during the finalizing stage NotifComparator comparator = new HypeComparator(PACKAGE_5); OnBeforeRenderListListener listener = - (list) -> comparator.invalidateList(); + (list) -> comparator.invalidateList(null); mListBuilder.setComparators(singletonList(comparator)); mListBuilder.addOnBeforeRenderListListener(listener); @@ -1766,7 +1766,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { public void testOutOfOrderPreRenderFilterInvalidationThrows() { // GIVEN a PreRenderNotifFilter that gets invalidated during the finalizing stage NotifFilter filter = new PackageFilter(PACKAGE_5); - OnBeforeRenderListListener listener = (list) -> filter.invalidateList(); + OnBeforeRenderListListener listener = (list) -> filter.invalidateList(null); mListBuilder.addFinalizeFilter(filter); mListBuilder.addOnBeforeRenderListListener(listener); @@ -1903,7 +1903,7 @@ public class ShadeListBuilderTest extends SysuiTestCase { public void testInOrderPreRenderFilter() { // GIVEN a PreRenderFilter that gets invalidated during the grouping stage NotifFilter filter = new PackageFilter(PACKAGE_5); - OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList(); + OnBeforeTransformGroupsListener listener = (list) -> filter.invalidateList(null); mListBuilder.addFinalizeFilter(filter); mListBuilder.addOnBeforeTransformGroupsListener(listener); @@ -1936,8 +1936,8 @@ public class ShadeListBuilderTest extends SysuiTestCase { mListBuilder.addFinalizeFilter(filter2); // WHEN both filters invalidate - filter1.invalidateList(); - filter2.invalidateList(); + filter1.invalidateList(null); + filter2.invalidateList(null); // THEN the pipeline choreographer is scheduled to evaluate, AND the pipeline hasn't // actually run. diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java index d21053bcd691..27542a462d36 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HideNotifsForOtherUsersCoordinatorTest.java @@ -18,7 +18,9 @@ package com.android.systemui.statusbar.notification.collection.coordinator; 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.verify; import static org.mockito.Mockito.when; @@ -51,7 +53,6 @@ public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase { @Mock private NotificationLockscreenUserManager mLockscreenUserManager; @Mock private NotifPipeline mNotifPipeline; @Mock private PluggableListener<NotifFilter> mInvalidationListener; - @Mock private SharedCoordinatorLogger mLogger; @Captor private ArgumentCaptor<UserChangedListener> mUserChangedListenerCaptor; @Captor private ArgumentCaptor<NotifFilter> mNotifFilterCaptor; @@ -66,7 +67,7 @@ public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); HideNotifsForOtherUsersCoordinator coordinator = - new HideNotifsForOtherUsersCoordinator(mLockscreenUserManager, mLogger); + new HideNotifsForOtherUsersCoordinator(mLockscreenUserManager); coordinator.attach(mNotifPipeline); verify(mLockscreenUserManager).addUserChangedListener(mUserChangedListenerCaptor.capture()); @@ -102,6 +103,6 @@ public class HideNotifsForOtherUsersCoordinatorTest extends SysuiTestCase { mCapturedUserChangeListener.onCurrentProfilesChanged(new SparseArray<>()); // THEN the filter is invalidated - verify(mInvalidationListener).onPluggableInvalidated(mCapturedNotifFilter); + verify(mInvalidationListener).onPluggableInvalidated(eq(mCapturedNotifFilter), any()); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt index 8c506a6d16ae..7e2e6f619946 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt @@ -41,7 +41,6 @@ class KeyguardCoordinatorTest : SysuiTestCase() { private val notifPipeline: NotifPipeline = mock() private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock() private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock() - private val sharedCoordinatorLogger: SharedCoordinatorLogger = mock() private val statusBarStateController: StatusBarStateController = mock() private lateinit var onStateChangeListener: Consumer<String> @@ -52,7 +51,6 @@ class KeyguardCoordinatorTest : SysuiTestCase() { val keyguardCoordinator = KeyguardCoordinator( keyguardNotifVisibilityProvider, sectionHeaderVisibilityProvider, - sharedCoordinatorLogger, statusBarStateController ) keyguardCoordinator.attach(notifPipeline) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java index a6d3719de5cc..f4adf6927e31 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java @@ -463,7 +463,8 @@ public class PreparationCoordinatorTest extends SysuiTestCase { } @Override - public void abortInflation(@NonNull NotificationEntry entry) { + public boolean abortInflation(@NonNull NotificationEntry entry) { + return false; } public InflationCallback getInflateCallback(NotificationEntry entry) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt index a2d8e3ddba24..27be4c802aec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.withArgCaptor import dagger.BindsInstance @@ -79,7 +80,7 @@ class SensitiveContentCoordinatorTest : SysuiTestCase() { dynamicPrivacyListener.onDynamicPrivacyChanged() - verify(invalidationListener).onPluggableInvalidated(invalidator) + verify(invalidationListener).onPluggableInvalidated(eq(invalidator), any()) } @Test @@ -265,4 +266,4 @@ interface TestSensitiveContentCoordinatorComponent { @BindsInstance keyguardStateController: KeyguardStateController ): TestSensitiveContentCoordinatorComponent } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt index fdff6e9a52f3..d4f05050b6dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt @@ -35,21 +35,23 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.withArgCaptor import com.android.systemui.util.time.FakeSystemClock +import java.util.concurrent.TimeUnit import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.anyString import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations -import java.util.concurrent.TimeUnit @SmallTest class SmartspaceDedupingCoordinatorTest : SysuiTestCase() { @@ -349,7 +351,7 @@ class SmartspaceDedupingCoordinatorTest : SysuiTestCase() { // THEN the new pipeline is invalidated (but the old one isn't because it's not // necessary) because the notif should no longer be filtered out - verify(pluggableListener).onPluggableInvalidated(filter) + verify(pluggableListener).onPluggableInvalidated(eq(filter), any()) verify(notificationEntryManager, never()).updateNotifications(anyString()) assertFalse(filter.shouldFilterOut(entry2HasNotRecentlyAlerted, now)) } @@ -387,7 +389,7 @@ class SmartspaceDedupingCoordinatorTest : SysuiTestCase() { } private fun verifyPipelinesInvalidated() { - verify(pluggableListener).onPluggableInvalidated(filter) + verify(pluggableListener).onPluggableInvalidated(eq(filter), any()) verify(notificationEntryManager).updateNotifications(anyString()) } @@ -396,7 +398,7 @@ class SmartspaceDedupingCoordinatorTest : SysuiTestCase() { } private fun verifyPipelinesNotInvalidated() { - verify(pluggableListener, never()).onPluggableInvalidated(filter) + verify(pluggableListener, never()).onPluggableInvalidated(eq(filter), any()) verify(notificationEntryManager, never()).updateNotifications(anyString()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt index 5ba926e29087..0830191fe035 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt @@ -60,7 +60,6 @@ class ViewConfigCoordinatorTest : SysuiTestCase() { @Before fun setUp() { - whenever(pipeline.isNewPipelineEnabled).thenReturn(true) whenever(pipeline.allNotifs).thenReturn(listOf(entry)) whenever(entry.row).thenReturn(row) coordinator = ViewConfigCoordinator( 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 2f37f8928e79..c961cec39208 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 @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator; import static junit.framework.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; @@ -35,6 +36,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotifPanelEvents; import com.android.systemui.statusbar.notification.collection.GroupEntry; import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder; import com.android.systemui.statusbar.notification.collection.NotifPipeline; @@ -43,7 +45,6 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifStabilityManager; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable; 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.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -55,6 +56,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.verification.VerificationMode; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -130,7 +132,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { doAnswer(i -> { mNotifStabilityManager.onBeginRun(); return null; - }).when(mInvalidateListener).onPluggableInvalidated(eq(mNotifStabilityManager)); + }).when(mInvalidateListener).onPluggableInvalidated(eq(mNotifStabilityManager), any()); } @Test @@ -280,7 +282,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.uptimeMillis()); // THEN the notification list is invalidated - verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(times(1)); } @Test @@ -295,7 +297,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis()); // THEN invalidate is not called because this entry was never suppressed from reordering - verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(never()); } @Test @@ -312,7 +314,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { // THEN invalidate is not called because this entry was never suppressed from reordering; // THEN section changes are allowed for this notification - verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(never()); assertTrue(mNotifStabilityManager.isSectionChangeAllowed(mEntry)); // WHEN we're pulsing (now disallowing reordering) @@ -341,13 +343,13 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { // WHEN we temporarily allow section changes for this notification entry mCoordinator.temporarilyAllowSectionChanges(mEntry, mFakeSystemClock.currentTimeMillis()); // can now reorder, so invalidates - verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(times(1)); // WHEN reordering is now allowed because device isn't pulsing anymore setPulsing(false); // THEN invalidate isn't called a second time since reordering was already allowed - verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(times(1)); } @Test @@ -368,7 +370,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { // THEN we never see any calls to invalidate since there weren't any notifications that // were being suppressed from grouping or section changes - verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(never()); } @Test @@ -386,7 +388,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { setPanelExpanded(false); // invalidate is called because we were previously suppressing a group change - verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(times(1)); } @Test @@ -400,7 +402,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { setActivityLaunching(false); // invalidate is called, b/c we were previously suppressing the pipeline from running - verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(times(1)); } @Test @@ -414,7 +416,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { setPanelCollapsing(false); // invalidate is called, b/c we were previously suppressing the pipeline from running - verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(times(1)); } @Test @@ -426,7 +428,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { setPanelCollapsing(false); // THEN invalidate is not called, b/c nothing has been suppressed - verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(never()); } @Test @@ -438,7 +440,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { setActivityLaunching(false); // THEN invalidate is not called, b/c nothing has been suppressed - verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(never()); } @Test @@ -457,7 +459,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { setPanelExpanded(false); // invalidate is called because we were previously suppressing an entry reorder - verify(mInvalidateListener, times(1)).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(times(1)); } @Test @@ -474,7 +476,7 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { setPanelExpanded(false); // invalidate is not called because we were not told that an entry reorder was suppressed - verify(mInvalidateListener, never()).onPluggableInvalidated(mNotifStabilityManager); + verifyStabilityManagerWasInvalidated(never()); } @Test @@ -499,6 +501,10 @@ public class VisualStabilityCoordinatorTest extends SysuiTestCase { assertFalse(mNotifStabilityManager.isGroupPruneAllowed(mGroupEntry)); } + private void verifyStabilityManagerWasInvalidated(VerificationMode mode) { + verify(mInvalidateListener, mode).onPluggableInvalidated(eq(mNotifStabilityManager), any()); + } + private void setActivityLaunching(boolean activityLaunching) { mNotifPanelEventsCallback.onLaunchingActivityChanged(activityLaunching); } 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 3f56cf9cb8ad..72d3c2e95d75 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 @@ -25,6 +25,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking; +import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; import static com.android.systemui.statusbar.StatusBarState.SHADE; import static com.google.common.truth.Truth.assertThat; @@ -32,6 +33,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -96,6 +98,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { NotifPipelineFlags mFlags; @Mock KeyguardNotificationVisibilityProvider mKeyguardNotificationVisibilityProvider; + @Mock + PendingIntent mPendingIntent; private NotificationInterruptStateProviderImpl mNotifInterruptionStateProvider; @@ -197,7 +201,7 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { ensureStateForHeadsUpWhenAwake(); // WHEN this entry should be filtered out - NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); + NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); when(mNotificationFilter.shouldFilterOut(entry)).thenReturn(true); // THEN we shouldn't heads up this entry @@ -206,11 +210,9 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { @Test public void testDoNotRunFilterOnNewPipeline() { - when(mFlags.isNewPipelineEnabled()).thenReturn(true); // WHEN this entry should be filtered out - NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); + NotificationEntry entry = createNotification(IMPORTANCE_DEFAULT); mNotifInterruptionStateProvider.shouldHeadsUp(entry); - verify(mFlags, times(1)).isNewPipelineEnabled(); verify(mNotificationFilter, times(0)).shouldFilterOut(eq(entry)); } @@ -326,7 +328,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { public void testShouldNotHeadsUp_filtered() throws RemoteException { ensureStateForHeadsUpWhenAwake(); // Make canAlertCommon false by saying it's filtered out - when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true); + when(mKeyguardNotificationVisibilityProvider.shouldHideNotification(any())) + .thenReturn(true); NotificationEntry entry = createNotification(IMPORTANCE_HIGH); assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); @@ -423,6 +426,122 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { assertThat(mNotifInterruptionStateProvider.shouldHeadsUp(entry)).isFalse(); } + @Test + public void testShouldNotFullScreen_notPendingIntent() throws RemoteException { + NotificationEntry entry = createNotification(IMPORTANCE_HIGH); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + verify(mLogger, never()).logNoFullscreen(any(), any()); + verify(mLogger, never()).logNoFullscreenWarning(any(), any()); + verify(mLogger, never()).logFullscreen(any(), any()); + } + + @Test + public void testShouldNotFullScreen_notHighImportance() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_DEFAULT, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + verify(mLogger).logNoFullscreen(entry, "Not important enough"); + verify(mLogger, never()).logNoFullscreenWarning(any(), any()); + verify(mLogger, never()).logFullscreen(any(), any()); + } + + @Test + public void testShouldNotFullScreen_isGroupAlertSilenced() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ true); + when(mPowerManager.isInteractive()).thenReturn(false); + when(mDreamManager.isDreaming()).thenReturn(true); + when(mStatusBarStateController.getState()).thenReturn(KEYGUARD); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + verify(mLogger, never()).logNoFullscreen(any(), any()); + verify(mLogger).logNoFullscreenWarning(entry, "GroupAlertBehavior will prevent HUN"); + verify(mLogger, never()).logFullscreen(any(), any()); + } + + @Test + public void testShouldFullScreen_notInteractive() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(false); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + verify(mLogger, never()).logNoFullscreen(any(), any()); + verify(mLogger, never()).logNoFullscreenWarning(any(), any()); + verify(mLogger).logFullscreen(entry, "Device is not interactive"); + } + + @Test + public void testShouldFullScreen_isDreaming() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(true); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + verify(mLogger, never()).logNoFullscreen(any(), any()); + verify(mLogger, never()).logNoFullscreenWarning(any(), any()); + verify(mLogger).logFullscreen(entry, "Device is dreaming"); + } + + @Test + public void testShouldFullScreen_onKeyguard() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(KEYGUARD); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + verify(mLogger, never()).logNoFullscreen(any(), any()); + verify(mLogger, never()).logNoFullscreenWarning(any(), any()); + verify(mLogger).logFullscreen(entry, "Keyguard is showing"); + } + + @Test + public void testShouldNotFullScreen_willHun() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mPowerManager.isScreenOn()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isFalse(); + verify(mLogger).logNoFullscreen(entry, "Expected to HUN"); + verify(mLogger, never()).logNoFullscreenWarning(any(), any()); + verify(mLogger, never()).logFullscreen(any(), any()); + } + + @Test + public void testShouldFullScreen_packageSnoozed() throws RemoteException { + NotificationEntry entry = createFsiNotification(IMPORTANCE_HIGH, /* silenced */ false); + when(mPowerManager.isInteractive()).thenReturn(true); + when(mPowerManager.isScreenOn()).thenReturn(true); + when(mDreamManager.isDreaming()).thenReturn(false); + when(mStatusBarStateController.getState()).thenReturn(SHADE); + when(mHeadsUpManager.isSnoozed("a")).thenReturn(true); + + assertThat(mNotifInterruptionStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) + .isTrue(); + verify(mLogger).logNoHeadsUpPackageSnoozed(entry); + verify(mLogger, never()).logNoFullscreen(any(), any()); + verify(mLogger, never()).logNoFullscreenWarning(any(), any()); + verify(mLogger).logFullscreen(entry, "Expected not to HUN"); + } + /** * Bubbles can happen. */ @@ -504,7 +623,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { ensureStateForBubbleUp(); // Make canAlertCommon false by saying it's filtered out - when(mNotificationFilter.shouldFilterOut(any())).thenReturn(true); + when(mKeyguardNotificationVisibilityProvider.shouldHideNotification(any())) + .thenReturn(true); assertThat(mNotifInterruptionStateProvider.shouldBubbleUp(createBubble())).isFalse(); } @@ -516,8 +636,8 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { 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)) + PendingIntent.FLAG_MUTABLE), + Icon.createWithResource(mContext.getResources(), R.drawable.android)) .build(); Notification.Builder nb = new Notification.Builder(getContext(), "a") .setContentTitle("title") @@ -549,6 +669,10 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { .setContentText("content text") .build(); + return createNotification(importance, n); + } + + private NotificationEntry createNotification(int importance, Notification n) { return new NotificationEntryBuilder() .setPkg("a") .setOpPkg("a") @@ -559,45 +683,57 @@ public class NotificationInterruptStateProviderImplTest extends SysuiTestCase { .build(); } + private NotificationEntry createFsiNotification(int importance, boolean silent) { + Notification n = new Notification.Builder(getContext(), "a") + .setContentTitle("title") + .setContentText("content text") + .setFullScreenIntent(mPendingIntent, true) + .setGroup("fsi") + .setGroupAlertBehavior(silent ? GROUP_ALERT_SUMMARY : Notification.GROUP_ALERT_ALL) + .build(); + + return createNotification(importance, n); + } + private final NotificationInterruptSuppressor mSuppressAwakeHeadsUp = new NotificationInterruptSuppressor() { - @Override - public String getName() { - return "suppressAwakeHeadsUp"; - } + @Override + public String getName() { + return "suppressAwakeHeadsUp"; + } - @Override - public boolean suppressAwakeHeadsUp(NotificationEntry entry) { - return true; - } - }; + @Override + public boolean suppressAwakeHeadsUp(NotificationEntry entry) { + return true; + } + }; private final NotificationInterruptSuppressor mSuppressAwakeInterruptions = new NotificationInterruptSuppressor() { - @Override - public String getName() { - return "suppressAwakeInterruptions"; - } + @Override + public String getName() { + return "suppressAwakeInterruptions"; + } - @Override - public boolean suppressAwakeInterruptions(NotificationEntry entry) { - return true; - } - }; + @Override + public boolean suppressAwakeInterruptions(NotificationEntry entry) { + return true; + } + }; private final NotificationInterruptSuppressor mSuppressInterruptions = new NotificationInterruptSuppressor() { - @Override - public String getName() { - return "suppressInterruptions"; - } - - @Override - public boolean suppressInterruptions(NotificationEntry entry) { - return true; - } - }; + @Override + public String getName() { + return "suppressInterruptions"; + } + + @Override + public boolean suppressInterruptions(NotificationEntry entry) { + return true; + } + }; } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java deleted file mode 100644 index 429d2ed36cc7..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerLegacyTest.java +++ /dev/null @@ -1,299 +0,0 @@ -/* - * 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. - * 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.notification.logging; - -import static org.junit.Assert.assertArrayEquals; -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.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.app.Notification; -import android.os.Handler; -import android.os.Looper; -import android.os.UserHandle; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; - -import androidx.test.filters.SmallTest; - -import com.android.internal.logging.InstanceId; -import com.android.internal.statusbar.IStatusBarService; -import com.android.internal.statusbar.NotificationVisibility; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.NotificationListener; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; -import com.android.systemui.statusbar.notification.NotificationEntryManager; -import com.android.systemui.statusbar.notification.collection.NotifLiveData; -import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore; -import com.android.systemui.statusbar.notification.collection.NotifPipeline; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; -import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; -import com.android.systemui.statusbar.notification.logging.nano.Notifications; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; -import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; - -import com.google.android.collect.Lists; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executor; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper -public class NotificationLoggerLegacyTest extends SysuiTestCase { - private static final String TEST_PACKAGE_NAME = "test"; - private static final int TEST_UID = 0; - - @Mock private NotificationListContainer mListContainer; - @Mock private IStatusBarService mBarService; - @Mock private ExpandableNotificationRow mRow; - @Mock private NotificationLogger.ExpansionStateLogger mExpansionStateLogger; - - // Dependency mocks: - @Mock private NotifPipelineFlags mNotifPipelineFlags; - @Mock private NotifLiveDataStore mNotifLiveDataStore; - @Mock private NotifLiveData<List<NotificationEntry>> mActiveNotifList; - @Mock private NotificationVisibilityProvider mVisibilityProvider; - @Mock private NotificationEntryManager mEntryManager; - @Mock private NotifPipeline mNotifPipeline; - @Mock private NotificationListener mListener; - - private NotificationEntry mEntry; - private TestableNotificationLogger mLogger; - private ConcurrentLinkedQueue<AssertionError> mErrorQueue = new ConcurrentLinkedQueue<>(); - private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); - private NotificationPanelLoggerFake mNotificationPanelLoggerFake = - new NotificationPanelLoggerFake(); - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(mNotifLiveDataStore.getActiveNotifList()).thenReturn(mActiveNotifList); - - mEntry = new NotificationEntryBuilder() - .setPkg(TEST_PACKAGE_NAME) - .setOpPkg(TEST_PACKAGE_NAME) - .setUid(TEST_UID) - .setNotification(new Notification()) - .setUser(UserHandle.CURRENT) - .setInstanceId(InstanceId.fakeInstanceId(1)) - .build(); - mEntry.setRow(mRow); - - mLogger = new TestableNotificationLogger( - mListener, - mUiBgExecutor, - mNotifPipelineFlags, - mNotifLiveDataStore, - mVisibilityProvider, - mEntryManager, - mNotifPipeline, - mock(StatusBarStateControllerImpl.class), - mBarService, - mExpansionStateLogger - ); - mLogger.setUpWithContainer(mListContainer); - verify(mEntryManager).addNotificationEntryListener(any()); - verify(mNotifPipeline, never()).addCollectionListener(any()); - } - - @After - public void tearDown() { - mLogger.mHandler.removeCallbacksAndMessages(null); - } - - @Test - public void testOnChildLocationsChangedReportsVisibilityChanged() throws Exception { - NotificationVisibility[] newlyVisibleKeys = { - NotificationVisibility.obtain(mEntry.getKey(), 0, 1, true) - }; - NotificationVisibility[] noLongerVisibleKeys = {}; - doAnswer(invocation -> { - try { - assertArrayEquals(newlyVisibleKeys, - (NotificationVisibility[]) invocation.getArguments()[0]); - assertArrayEquals(noLongerVisibleKeys, - (NotificationVisibility[]) invocation.getArguments()[1]); - } catch (AssertionError error) { - mErrorQueue.offer(error); - } - return null; - } - ).when(mBarService).onNotificationVisibilityChanged(any(NotificationVisibility[].class), - any(NotificationVisibility[].class)); - - when(mListContainer.isInVisibleLocation(any())).thenReturn(true); - when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry)); - mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(); - TestableLooper.get(this).processAllMessages(); - mUiBgExecutor.runAllReady(); - - if (!mErrorQueue.isEmpty()) { - throw mErrorQueue.poll(); - } - - // |mEntry| won't change visibility, so it shouldn't be reported again: - Mockito.reset(mBarService); - mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(); - TestableLooper.get(this).processAllMessages(); - mUiBgExecutor.runAllReady(); - - verify(mBarService, never()).onNotificationVisibilityChanged(any(), any()); - } - - @Test - public void testStoppingNotificationLoggingReportsCurrentNotifications() - throws Exception { - when(mListContainer.isInVisibleLocation(any())).thenReturn(true); - when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry)); - mLogger.getChildLocationsChangedListenerForTest().onChildLocationsChanged(); - TestableLooper.get(this).processAllMessages(); - mUiBgExecutor.runAllReady(); - Mockito.reset(mBarService); - - setStateAsleep(); - mLogger.onDozingChanged(false); // Wake to lockscreen - mLogger.onDozingChanged(true); // And go back to sleep, turning off logging - mUiBgExecutor.runAllReady(); - // The visibility objects are recycled by NotificationLogger, so we can't use specific - // matchers here. - verify(mBarService, times(1)).onNotificationVisibilityChanged(any(), any()); - } - - private void setStateAsleep() { - mLogger.onPanelExpandedChanged(true); - mLogger.onDozingChanged(true); - mLogger.onStateChanged(StatusBarState.KEYGUARD); - } - - private void setStateAwake() { - mLogger.onPanelExpandedChanged(false); - mLogger.onDozingChanged(false); - mLogger.onStateChanged(StatusBarState.SHADE); - } - - @Test - public void testLogPanelShownOnWake() { - when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry)); - setStateAsleep(); - mLogger.onDozingChanged(false); // Wake to lockscreen - assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); - assertTrue(mNotificationPanelLoggerFake.get(0).isLockscreen); - assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); - Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; - assertEquals(TEST_PACKAGE_NAME, n.packageName); - assertEquals(TEST_UID, n.uid); - assertEquals(1, n.instanceId); - assertFalse(n.isGroupSummary); - assertEquals(Notifications.Notification.SECTION_ALERTING, n.section); - } - - @Test - public void testLogPanelShownOnShadePull() { - when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(mEntry)); - setStateAwake(); - // Now expand panel - mLogger.onPanelExpandedChanged(true); - assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); - assertFalse(mNotificationPanelLoggerFake.get(0).isLockscreen); - assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); - Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; - assertEquals(TEST_PACKAGE_NAME, n.packageName); - assertEquals(TEST_UID, n.uid); - assertEquals(1, n.instanceId); - assertFalse(n.isGroupSummary); - assertEquals(Notifications.Notification.SECTION_ALERTING, n.section); - } - - - @Test - public void testLogPanelShownHandlesNullInstanceIds() { - // Construct a NotificationEntry like mEntry, but with a null instance id. - NotificationEntry entry = new NotificationEntryBuilder() - .setPkg(TEST_PACKAGE_NAME) - .setOpPkg(TEST_PACKAGE_NAME) - .setUid(TEST_UID) - .setNotification(new Notification()) - .setUser(UserHandle.CURRENT) - .build(); - entry.setRow(mRow); - - when(mActiveNotifList.getValue()).thenReturn(Lists.newArrayList(entry)); - setStateAsleep(); - mLogger.onDozingChanged(false); // Wake to lockscreen - assertEquals(1, mNotificationPanelLoggerFake.getCalls().size()); - assertEquals(1, mNotificationPanelLoggerFake.get(0).list.notifications.length); - Notifications.Notification n = mNotificationPanelLoggerFake.get(0).list.notifications[0]; - assertEquals(0, n.instanceId); - } - - private class TestableNotificationLogger extends NotificationLogger { - - TestableNotificationLogger(NotificationListener notificationListener, - Executor uiBgExecutor, - NotifPipelineFlags notifPipelineFlags, - NotifLiveDataStore notifLiveDataStore, - NotificationVisibilityProvider visibilityProvider, - NotificationEntryManager entryManager, - NotifPipeline notifPipeline, - StatusBarStateControllerImpl statusBarStateController, - IStatusBarService barService, - ExpansionStateLogger expansionStateLogger) { - super( - notificationListener, - uiBgExecutor, - notifPipelineFlags, - notifLiveDataStore, - visibilityProvider, - entryManager, - notifPipeline, - statusBarStateController, - expansionStateLogger, - mNotificationPanelLoggerFake - ); - mBarService = barService; - mHandler.removeCallbacksAndMessages(null); - // Make this on the current thread so we can wait for it during tests. - mHandler = Handler.createAsync(Looper.myLooper()); - } - - OnChildLocationsChangedListener getChildLocationsChangedListenerForTest() { - return mNotificationLocationsChangedListener; - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java index b69bd8dfca9c..8a7b9d3b6024 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/NotificationLoggerTest.java @@ -103,7 +103,6 @@ public class NotificationLoggerTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(true); when(mNotifLiveDataStore.getActiveNotifList()).thenReturn(mActiveNotifEntries); mEntry = new NotificationEntryBuilder() @@ -278,10 +277,8 @@ public class NotificationLoggerTest extends SysuiTestCase { super( notificationListener, uiBgExecutor, - notifPipelineFlags, notifLiveDataStore, visibilityProvider, - entryManager, notifPipeline, statusBarStateController, expansionStateLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java index 251ac7d250fe..bf7549a23707 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java @@ -194,7 +194,8 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase { mLeakDetector, mock(IStatusBarService.class), NotifLiveDataStoreMocksKt.createNotifLiveDataStoreImplMock(), - mock(DumpManager.class) + mock(DumpManager.class), + mBgExecutor ); mEntryManager.initialize( mNotificationListener, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 4efd5c995a97..c199147b4f79 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -52,6 +52,7 @@ import com.android.systemui.media.KeyguardMediaController; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.OnMenuEventListener; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.transition.ShadeTransitionController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener; @@ -75,7 +76,6 @@ import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.ShadeController; -import com.android.systemui.statusbar.phone.shade.transition.ShadeTransitionController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.ZenModeController; @@ -150,7 +150,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); when(mNotificationSwipeHelperBuilder.build()).thenReturn(mNotificationSwipeHelper); - when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false); mController = new NotificationStackScrollLayoutController( true, @@ -166,7 +165,6 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mKeyguardMediaController, mKeyguardBypassController, mZenModeController, - mColorExtractor, mNotificationLockscreenUserManager, mMetricsLogger, mDumpManager, @@ -179,15 +177,12 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { mLegacyGroupManager, mLegacyGroupManager, mSilentHeaderController, - mNotifPipelineFlags, mNotifPipeline, mNotifCollection, mEntryManager, mLockscreenShadeTransitionController, mShadeTransitionController, - mIStatusBarService, mUiEventLogger, - mLayoutInflater, mRemoteInputManager, mVisualStabilityManager, mShadeController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java index 9bfb2c4ce00c..d79f3361408b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java @@ -37,6 +37,8 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.SysuiTestCase; import com.android.systemui.assist.AssistManager; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DisableFlagsLogger; import com.android.systemui.statusbar.StatusBarStateControllerImpl; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 2e26a2be4382..b75c52ad283e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -19,6 +19,8 @@ package com.android.systemui.statusbar.phone; import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.fail; @@ -44,6 +46,7 @@ import android.app.WallpaperManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; +import android.content.Intent; import android.content.IntentFilter; import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.AmbientDisplayConfiguration; @@ -89,7 +92,6 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.demomode.DemoModeController; -import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.fragments.FragmentService; @@ -103,6 +105,10 @@ import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.settings.brightness.BrightnessSliderController; +import com.android.systemui.shade.NotificationPanelView; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; +import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; @@ -118,7 +124,6 @@ import com.android.systemui.statusbar.OperatorNameViewController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.StatusBarStateControllerImpl; -import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationEntryManager; @@ -129,7 +134,6 @@ import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.legacy.VisualStabilityManager; -import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.init.NotificationsController; import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider; @@ -159,7 +163,6 @@ import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.concurrency.MessageRouterImpl; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.volume.VolumeComponent; -import com.android.systemui.wmshell.BubblesManager; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.StartingSurface; @@ -223,7 +226,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private NotificationShadeWindowView mNotificationShadeWindowView; @Mock private BroadcastDispatcher mBroadcastDispatcher; @Mock private AssistManager mAssistManager; - @Mock private NotifShadeEventSource mNotifShadeEventSource; @Mock private NotificationEntryManager mNotificationEntryManager; @Mock private NotificationGutsManager mNotificationGutsManager; @Mock private NotificationMediaManager mNotificationMediaManager; @@ -240,15 +242,12 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private StatusBarWindowStateController mStatusBarWindowStateController; @Mock private NotificationViewHierarchyManager mNotificationViewHierarchyManager; @Mock private UserSwitcherController mUserSwitcherController; - @Mock private NetworkController mNetworkController; - @Mock private BubblesManager mBubblesManager; @Mock private Bubbles mBubbles; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController; @Mock private DozeParameters mDozeParameters; @Mock private Lazy<LockscreenWallpaper> mLockscreenWallpaperLazy; - @Mock private LockscreenGestureLogger mLockscreenGestureLogger; @Mock private LockscreenWallpaper mLockscreenWallpaper; @Mock private DozeServiceHost mDozeServiceHost; @Mock private ViewMediatorCallback mKeyguardVieMediatorCallback; @@ -286,7 +285,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private NotifLiveDataStore mNotifLiveDataStore; @Mock private InteractionJankMonitor mJankMonitor; @Mock private DeviceStateManager mDeviceStateManager; - @Mock private DreamOverlayStateController mDreamOverlayStateController; @Mock private WiredChargingRippleController mWiredChargingRippleController; private ShadeController mShadeController; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); @@ -322,10 +320,8 @@ public class CentralSurfacesImplTest extends SysuiTestCase { NotificationLogger notificationLogger = new NotificationLogger( mNotificationListener, mUiBgExecutor, - mNotifPipelineFlags, mNotifLiveDataStore, mVisibilityProvider, - mock(NotificationEntryManager.class), mock(NotifPipeline.class), mStatusBarStateController, mExpansionStateLogger, @@ -403,7 +399,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { new FalsingManagerFake(), new FalsingCollectorFake(), mBroadcastDispatcher, - mNotifShadeEventSource, mNotificationEntryManager, mNotificationGutsManager, notificationLogger, @@ -418,13 +413,11 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mLockscreenUserManager, mRemoteInputManager, mUserSwitcherController, - mNetworkController, mBatteryController, mColorExtractor, new ScreenLifecycle(mDumpManager), wakefulnessLifecycle, mStatusBarStateController, - Optional.of(mBubblesManager), Optional.of(mBubbles), mVisualStabilityManager, mDeviceProvisionedController, @@ -436,7 +429,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mDozeParameters, mScrimController, mLockscreenWallpaperLazy, - mLockscreenGestureLogger, mBiometricUnlockControllerLazy, mDozeServiceHost, mPowerManager, mScreenPinningRequest, @@ -468,7 +460,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mLockscreenTransitionController, mFeatureFlags, mKeyguardUnlockAnimationController, - new Handler(TestableLooper.get(this).getLooper()), mMainExecutor, new MessageRouterImpl(mMainExecutor), mWallpaperManager, @@ -477,7 +468,6 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mNotifPipelineFlags, mJankMonitor, mDeviceStateManager, - mDreamOverlayStateController, mWiredChargingRippleController, mDreamManager); when(mKeyguardViewMediator.registerCentralSurfaces( any(CentralSurfacesImpl.class), @@ -1001,6 +991,22 @@ public class CentralSurfacesImplTest extends SysuiTestCase { verify(mStatusBarStateController, never()).setLeaveOpenOnKeyguardHide(true); } + @Test + public void startActivityDismissingKeyguard_isShowingandIsOccluded() { + when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true); + when(mStatusBarKeyguardViewManager.isOccluded()).thenReturn(true); + mCentralSurfaces.startActivityDismissingKeyguard( + new Intent(), + /* onlyProvisioned = */false, + /* dismissShade = */false); + verify(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any(Runnable.class)); + ArgumentCaptor<OnDismissAction> onDismissActionCaptor = + ArgumentCaptor.forClass(OnDismissAction.class); + verify(mStatusBarKeyguardViewManager) + .dismissWithAction(onDismissActionCaptor.capture(), any(Runnable.class), eq(true)); + assertThat(onDismissActionCaptor.getValue().onDismiss()).isFalse(); + } + private void setDeviceState(int state) { ArgumentCaptor<DeviceStateManager.DeviceStateCallback> callbackCaptor = ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java index 26ac70c70e7f..5c9871a01536 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java @@ -41,6 +41,8 @@ import com.android.systemui.doze.DozeHost; import com.android.systemui.doze.DozeLog; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.PulseExpansionHandler; import com.android.systemui.statusbar.StatusBarState; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java index ed22cd3c55fc..103b7b4268de 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.java @@ -34,6 +34,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.HeadsUpStatusBarView; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt index 4b557dc423ff..3440fa5ac9b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt @@ -42,7 +42,6 @@ class KeyguardBottomAreaTest : SysuiTestCase() { mKeyguardBottomArea = LayoutInflater.from(mContext).inflate( R.layout.keyguard_bottom_area, null, false) as KeyguardBottomAreaView - mKeyguardBottomArea.setCentralSurfaces(mCentralSurfaces) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java index 39021d8732d3..60a3d95e24f6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java @@ -230,6 +230,15 @@ public class KeyguardBouncerTest extends SysuiTestCase { } @Test + public void show_notifiesKeyguardViewController() { + mBouncer.ensureView(); + + mBouncer.show(/* resetSecuritySelection= */ false); + + verify(mKeyguardHostViewController).onBouncerVisibilityChanged(View.VISIBLE); + } + + @Test public void testHide_notifiesFalsingManager() { mBouncer.hide(false); verify(mFalsingCollector).onBouncerHidden(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index 4e1a7088b17f..11e502fc79bf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -49,6 +49,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.battery.BatteryMeterViewController; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt deleted file mode 100644 index 7e245fcea22d..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewControllerTest.kt +++ /dev/null @@ -1,281 +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.statusbar.phone - -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper -import android.view.MotionEvent -import androidx.test.filters.SmallTest -import com.android.keyguard.LockIconViewController -import com.android.systemui.SysuiTestCase -import com.android.systemui.classifier.FalsingCollectorFake -import com.android.systemui.dock.DockManager -import com.android.systemui.keyguard.KeyguardUnlockAnimationController -import com.android.systemui.lowlightclock.LowLightClockController -import com.android.systemui.statusbar.LockscreenShadeTransitionController -import com.android.systemui.statusbar.NotificationShadeDepthController -import com.android.systemui.statusbar.NotificationShadeWindowController -import com.android.systemui.statusbar.SysuiStatusBarStateController -import com.android.systemui.statusbar.notification.stack.AmbientState -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController -import com.android.systemui.statusbar.phone.NotificationShadeWindowView.InteractionEventHandler -import com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManager -import com.android.systemui.statusbar.window.StatusBarWindowStateController -import com.android.systemui.tuner.TunerService -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.ArgumentMatchers -import org.mockito.Mock -import org.mockito.MockitoAnnotations -import org.mockito.Mockito.anyFloat -import org.mockito.Mockito.never -import org.mockito.Mockito.verify -import java.util.Optional -import org.mockito.Mockito.`when` as whenever - -@RunWith(AndroidTestingRunner::class) -@RunWithLooper(setAsMainLooper = true) -@SmallTest -class NotificationShadeWindowViewControllerTest : SysuiTestCase() { - private lateinit var mController: NotificationShadeWindowViewController - - @Mock - private lateinit var mView: NotificationShadeWindowView - @Mock - private lateinit var mTunerService: TunerService - @Mock - private lateinit var mStatusBarStateController: SysuiStatusBarStateController - @Mock - private lateinit var mCentralSurfaces: CentralSurfaces - @Mock - private lateinit var mDockManager: DockManager - @Mock - private lateinit var mNotificationPanelViewController: NotificationPanelViewController - @Mock - private lateinit var mNotificationShadeDepthController: NotificationShadeDepthController - @Mock - private lateinit var mNotificationShadeWindowController: NotificationShadeWindowController - @Mock - private lateinit var mKeyguardUnlockAnimationController: KeyguardUnlockAnimationController - @Mock - private lateinit var mAmbientState: AmbientState - @Mock - private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController - @Mock - private lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager - @Mock - private lateinit var mStatusBarWindowStateController: StatusBarWindowStateController - @Mock - private lateinit var mLockscreenShadeTransitionController: LockscreenShadeTransitionController - @Mock - private lateinit var mLockIconViewController: LockIconViewController - @Mock - private lateinit var mPhoneStatusBarViewController: PhoneStatusBarViewController - @Mock - private lateinit var mLowLightClockController: LowLightClockController - - private lateinit var mInteractionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler> - private lateinit var mInteractionEventHandler: InteractionEventHandler - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - whenever(mView.bottom).thenReturn(VIEW_BOTTOM) - - mController = NotificationShadeWindowViewController( - mLockscreenShadeTransitionController, - FalsingCollectorFake(), - mTunerService, - mStatusBarStateController, - mDockManager, - mNotificationShadeDepthController, - mView, - mNotificationPanelViewController, - PanelExpansionStateManager(), - stackScrollLayoutController, - mStatusBarKeyguardViewManager, - mStatusBarWindowStateController, - mLockIconViewController, - Optional.of(mLowLightClockController), - mCentralSurfaces, - mNotificationShadeWindowController, - mKeyguardUnlockAnimationController, - mAmbientState - ) - mController.setupExpandedStatusBar() - - mInteractionEventHandlerCaptor = - ArgumentCaptor.forClass(InteractionEventHandler::class.java) - verify(mView).setInteractionEventHandler(mInteractionEventHandlerCaptor.capture()) - mInteractionEventHandler = mInteractionEventHandlerCaptor.value - } - - // Note: So far, these tests only cover interactions with the status bar view controller. More - // tests need to be added to test the rest of handleDispatchTouchEvent. - - @Test - fun handleDispatchTouchEvent_nullStatusBarViewController_returnsFalse() { - mController.setStatusBarViewController(null) - - val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv) - - assertThat(returnVal).isFalse() - } - - @Test - fun handleDispatchTouchEvent_downTouchBelowView_sendsTouchToSb() { - mController.setStatusBarViewController(mPhoneStatusBarViewController) - val ev = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0) - whenever(mPhoneStatusBarViewController.sendTouchToView(ev)).thenReturn(true) - - val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(ev) - - verify(mPhoneStatusBarViewController).sendTouchToView(ev) - assertThat(returnVal).isTrue() - } - - @Test - fun handleDispatchTouchEvent_downTouchBelowViewThenAnotherTouch_sendsTouchToSb() { - mController.setStatusBarViewController(mPhoneStatusBarViewController) - val downEvBelow = MotionEvent.obtain( - 0L, 0L, MotionEvent.ACTION_DOWN, 0f, VIEW_BOTTOM + 4f, 0 - ) - mInteractionEventHandler.handleDispatchTouchEvent(downEvBelow) - - val nextEvent = MotionEvent.obtain( - 0L, 0L, MotionEvent.ACTION_MOVE, 0f, VIEW_BOTTOM + 5f, 0 - ) - whenever(mPhoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true) - - val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(nextEvent) - - verify(mPhoneStatusBarViewController).sendTouchToView(nextEvent) - assertThat(returnVal).isTrue() - } - - @Test - fun handleDispatchTouchEvent_downAndPanelCollapsedAndInSbBoundAndSbWindowShow_sendsTouchToSb() { - mController.setStatusBarViewController(mPhoneStatusBarViewController) - whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true) - whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true) - whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) - whenever(mPhoneStatusBarViewController.sendTouchToView(downEv)).thenReturn(true) - - val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv) - - verify(mPhoneStatusBarViewController).sendTouchToView(downEv) - assertThat(returnVal).isTrue() - } - - @Test - fun handleDispatchTouchEvent_panelNotCollapsed_returnsNull() { - mController.setStatusBarViewController(mPhoneStatusBarViewController) - whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true) - whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) - // Item we're testing - whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(false) - - val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv) - - verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv) - assertThat(returnVal).isNull() - } - - @Test - fun handleDispatchTouchEvent_touchNotInSbBounds_returnsNull() { - mController.setStatusBarViewController(mPhoneStatusBarViewController) - whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true) - whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true) - // Item we're testing - whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(false) - - val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv) - - verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv) - assertThat(returnVal).isNull() - } - - @Test - fun handleDispatchTouchEvent_sbWindowNotShowing_noSendTouchToSbAndReturnsTrue() { - mController.setStatusBarViewController(mPhoneStatusBarViewController) - whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true) - whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) - // Item we're testing - whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(false) - - val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(downEv) - - verify(mPhoneStatusBarViewController, never()).sendTouchToView(downEv) - assertThat(returnVal).isTrue() - } - - @Test - fun handleDispatchTouchEvent_downEventSentToSbThenAnotherEvent_sendsTouchToSb() { - mController.setStatusBarViewController(mPhoneStatusBarViewController) - whenever(mStatusBarWindowStateController.windowIsShowing()).thenReturn(true) - whenever(mNotificationPanelViewController.isFullyCollapsed).thenReturn(true) - whenever(mPhoneStatusBarViewController.touchIsWithinView(anyFloat(), anyFloat())) - .thenReturn(true) - - // Down event first - mInteractionEventHandler.handleDispatchTouchEvent(downEv) - - // Then another event - val nextEvent = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) - whenever(mPhoneStatusBarViewController.sendTouchToView(nextEvent)).thenReturn(true) - - val returnVal = mInteractionEventHandler.handleDispatchTouchEvent(nextEvent) - - verify(mPhoneStatusBarViewController).sendTouchToView(nextEvent) - assertThat(returnVal).isTrue() - } - - @Test - fun testLowLightClockAttachedWhenExpandedStatusBarSetup() { - verify(mLowLightClockController).attachLowLightClockView(ArgumentMatchers.any()) - } - - @Test - fun testLowLightClockShownWhenDozing() { - mController.setDozing(true) - verify(mLowLightClockController).showLowLightClock(true) - } - - @Test - fun testLowLightClockDozeTimeTickCalled() { - mController.dozeTimeTick() - verify(mLowLightClockController).dozeTimeTick() - } - - @Test - fun testLowLightClockHiddenWhenNotDozing() { - mController.setDozing(true) - verify(mLowLightClockController).showLowLightClock(true) - mController.setDozing(false) - verify(mLowLightClockController).showLowLightClock(false) - } -} - -private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) -private const val VIEW_BOTTOM = 100 diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 837b0453bc9a..7cd275db1ed6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -1553,6 +1553,15 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimInFront.shouldBlendWithMainColor()); } + @Test + public void applyState_unlocked_bouncerShowing() { + mScrimController.transitionTo(ScrimState.UNLOCKED); + mScrimController.setBouncerHiddenFraction(0.99f); + mScrimController.setRawPanelExpansionFraction(0f); + finishAnimationsImmediately(); + assertScrimAlpha(mScrimBehind, 0); + } + private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) { mScrimController.setRawPanelExpansionFraction(expansion); finishAnimationsImmediately(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt new file mode 100644 index 000000000000..d84010dc7771 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt @@ -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.systemui.statusbar.phone + +import android.graphics.Rect +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.view.View +import android.widget.FrameLayout +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.phone.StatusBarBoundsProvider.BoundsChangeListener +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.any +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) +@SmallTest +class StatusBarBoundsProviderTest : SysuiTestCase() { + + companion object { + private val START_SIDE_BOUNDS = Rect(50, 100, 150, 200) + private val END_SIDE_BOUNDS = Rect(250, 300, 350, 400) + } + + @Mock private lateinit var boundsChangeListener: BoundsChangeListener + + private lateinit var boundsProvider: StatusBarBoundsProvider + + private lateinit var startSideContent: View + private lateinit var endSideContent: View + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + startSideContent = spy(FrameLayout(context)).apply { setBoundsOnScreen(START_SIDE_BOUNDS) } + endSideContent = spy(FrameLayout(context)).apply { setBoundsOnScreen(END_SIDE_BOUNDS) } + + boundsProvider = + StatusBarBoundsProvider(setOf(boundsChangeListener), startSideContent, endSideContent) + } + + @Test + fun visibleStartSideBounds_returnsBoundsFromStartSideContentView() { + assertThat(boundsProvider.visibleStartSideBounds).isEqualTo(START_SIDE_BOUNDS) + } + + @Test + fun visibleEndSideBounds_returnsBoundsFromEndSideContentView() { + assertThat(boundsProvider.visibleEndSideBounds).isEqualTo(END_SIDE_BOUNDS) + } + + @Test + fun startBoundsChange_afterStart_notifiesListener() { + boundsProvider.start() + val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 } + + startSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener).onStatusBarBoundsChanged() + } + + @Test + fun startBoundsChange_beforeStart_doesNotNotifyListener() { + val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 } + + startSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } + + @Test + fun startBoundsChange_afterStop_doesNotNotifyListener() { + boundsProvider.start() + boundsProvider.stop() + val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 } + + startSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } + + @Test + fun startLayoutChange_afterStart_boundsOnScreenSame_doesNotNotifyListener() { + boundsProvider.start() + val newBounds = Rect(START_SIDE_BOUNDS).apply { left += 1 } + + startSideContent.layout(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } + + @Test + fun endBoundsChange_afterStart_notifiesListener() { + boundsProvider.start() + val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 } + + endSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener).onStatusBarBoundsChanged() + } + + @Test + fun endBoundsChange_beforeStart_doesNotNotifyListener() { + val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 } + + endSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } + + @Test + fun endBoundsChange_afterStop_doesNotNotifyListener() { + boundsProvider.start() + boundsProvider.stop() + val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 } + + endSideContent.setBoundsOnScreen(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } + + @Test + fun endLayoutChange_afterStart_boundsOnScreenSame_doesNotNotifyListener() { + boundsProvider.start() + val newBounds = Rect(START_SIDE_BOUNDS).apply { right += 1 } + + endSideContent.layout(newBounds) + + verify(boundsChangeListener, never()).onStatusBarBoundsChanged() + } +} + +private fun View.setBoundsOnScreen(bounds: Rect) { + doAnswer { invocation -> + val boundsOutput = invocation.arguments[0] as Rect + boundsOutput.set(bounds) + return@doAnswer Unit + } + .`when`(this) + .getBoundsOnScreen(any()) + layout(bounds.left, bounds.top, bounds.right, bounds.bottom) +} + +private fun View.layout(rect: Rect) { + layout(rect.left, rect.top, rect.right, rect.bottom) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconListTest.java index 4c20b61083f7..f0a4f3f2bf7a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconListTest.java @@ -1,4 +1,20 @@ -package com.android.systemui.statusbar; +/* + * 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.statusbar.phone; import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY; @@ -9,13 +25,10 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import android.test.suitebuilder.annotation.SmallTest; - +import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.phone.StatusBarIconHolder; -import com.android.systemui.statusbar.phone.StatusBarIconList; import com.android.systemui.statusbar.phone.StatusBarIconList.Slot; import org.junit.Test; @@ -33,28 +46,39 @@ public class StatusBarIconListTest extends SysuiTestCase { @Test public void testGetExistingSlot() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); - assertEquals(1, statusBarIconList.getSlotIndex("bbb")); - assertEquals(2, statusBarIconList.getSlotIndex("ccc")); + + List<Slot> slots = statusBarIconList.getSlots(); + assertEquals(3, slots.size()); + assertEquals("aaa", slots.get(0).getName()); + assertEquals("bbb", slots.get(1).getName()); + assertEquals("ccc", slots.get(2).getName()); } @Test public void testGetNonexistingSlot() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); - assertEquals(0, statusBarIconList.getSlotIndex("aaa")); - assertEquals(3, statusBarIconList.size()); - assertEquals(0, statusBarIconList.getSlotIndex("zzz")); // new content added in front - assertEquals(1, statusBarIconList.getSlotIndex("aaa")); // slid back - assertEquals(4, statusBarIconList.size()); + + statusBarIconList.getSlot("zzz"); + + List<Slot> slots = statusBarIconList.getSlots(); + assertEquals(4, slots.size()); + // new content added in front, so zzz should be first and aaa should slide back to second + assertEquals("zzz", slots.get(0).getName()); + assertEquals("aaa", slots.get(1).getName()); } @Test public void testAddSlotSlidesIcons() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class); - statusBarIconList.setIcon(0, sbHolder); - statusBarIconList.getSlotIndex("zzz"); // new content added in front - assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY)); - assertEquals(sbHolder, statusBarIconList.getIcon(1, TAG_PRIMARY)); + statusBarIconList.setIcon("aaa", sbHolder); + + statusBarIconList.getSlot("zzz"); + + List<Slot> slots = statusBarIconList.getSlots(); + // new content added in front, so the holder we set on "aaa" should show up at index 1 + assertNull(slots.get(0).getHolderForTag(TAG_PRIMARY)); + assertEquals(sbHolder, slots.get(1).getHolderForTag(TAG_PRIMARY)); } @Test @@ -62,11 +86,13 @@ public class StatusBarIconListTest extends SysuiTestCase { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class); StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class); - statusBarIconList.setIcon(0, sbHolderA); - statusBarIconList.setIcon(1, sbHolderB); - assertEquals(sbHolderA, statusBarIconList.getIcon(0, TAG_PRIMARY)); - assertEquals(sbHolderB, statusBarIconList.getIcon(1, TAG_PRIMARY)); - assertNull(statusBarIconList.getIcon(2, TAG_PRIMARY)); // icon not set + + statusBarIconList.setIcon("aaa", sbHolderA); + statusBarIconList.setIcon("bbb", sbHolderB); + + assertEquals(sbHolderA, statusBarIconList.getIconHolder("aaa", TAG_PRIMARY)); + assertEquals(sbHolderB, statusBarIconList.getIconHolder("bbb", TAG_PRIMARY)); + assertNull(statusBarIconList.getIconHolder("ccc", TAG_PRIMARY)); // icon not set } @Test @@ -74,24 +100,31 @@ public class StatusBarIconListTest extends SysuiTestCase { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class); StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class); - statusBarIconList.setIcon(0, sbHolderA); - statusBarIconList.setIcon(1, sbHolderB); - statusBarIconList.removeIcon(0, TAG_PRIMARY); - assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY)); // icon not set + + statusBarIconList.setIcon("aaa", sbHolderA); + statusBarIconList.setIcon("bbb", sbHolderB); + + statusBarIconList.removeIcon("aaa", TAG_PRIMARY); + + assertNull(statusBarIconList.getIconHolder("aaa", TAG_PRIMARY)); // icon not set } @Test public void testGetViewIndex_NoMultiples() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class); - statusBarIconList.setIcon(2, sbHolder); - // Icon for item 2 is 0th child view. - assertEquals(0, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); - statusBarIconList.setIcon(0, sbHolder); - // Icon for item 0 is 0th child view, - assertEquals(0, statusBarIconList.getViewIndex(0, TAG_PRIMARY)); - // and item 2 is now 1st child view. - assertEquals(1, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); + + statusBarIconList.setIcon("ccc", sbHolder); + + // Since only "ccc" has a holder set, it should be first + assertEquals(0, statusBarIconList.getViewIndex("ccc", TAG_PRIMARY)); + + // Now, also set a holder for "aaa" + statusBarIconList.setIcon("aaa", sbHolder); + + // Then "aaa" gets the first view index and "ccc" gets the second + assertEquals(0, statusBarIconList.getViewIndex("aaa", TAG_PRIMARY)); + assertEquals(1, statusBarIconList.getViewIndex("ccc", TAG_PRIMARY)); } @Test @@ -99,7 +132,7 @@ public class StatusBarIconListTest extends SysuiTestCase { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class); - statusBarIconList.setIcon(2, sbHolder); // item 2, one icon 0th child + statusBarIconList.setIcon("ccc", sbHolder); // All of these can be added to the same slot // no tag bc it defaults to 0 @@ -111,23 +144,23 @@ public class StatusBarIconListTest extends SysuiTestCase { int sb4Tag = 2; when(sbHolder4.getTag()).thenReturn(sb4Tag); - // Put a holder at slot 1, verify that it is first - statusBarIconList.setIcon(1, sbHolder2); - assertEquals(0, statusBarIconList.getViewIndex(1, TAG_PRIMARY)); - - // Put another holder at slot 1, verify it's index 0 and the rest come after - statusBarIconList.setIcon(1, sbHolder3); - assertEquals(0, statusBarIconList.getViewIndex(1, sb3Tag)); - assertEquals(1, statusBarIconList.getViewIndex(1, TAG_PRIMARY)); - // First icon should be at the end - assertEquals(2, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); - - // Put another one in there just for good measure - statusBarIconList.setIcon(1, sbHolder4); - assertEquals(0, statusBarIconList.getViewIndex(1, sb4Tag)); - assertEquals(1, statusBarIconList.getViewIndex(1, sb3Tag)); - assertEquals(2, statusBarIconList.getViewIndex(1, TAG_PRIMARY)); - assertEquals(3, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); + // Put a holder for "bbb", verify that it is first + statusBarIconList.setIcon("bbb", sbHolder2); + assertEquals(0, statusBarIconList.getViewIndex("bbb", TAG_PRIMARY)); + + // Put another holder for "bbb" at slot 1, verify its index 0 and the rest come after + statusBarIconList.setIcon("bbb", sbHolder3); + assertEquals(0, statusBarIconList.getViewIndex("bbb", sb3Tag)); + assertEquals(1, statusBarIconList.getViewIndex("bbb", TAG_PRIMARY)); + // "ccc" should appear at the end + assertEquals(2, statusBarIconList.getViewIndex("ccc", TAG_PRIMARY)); + + // Put another one in "bbb" just for good measure + statusBarIconList.setIcon("bbb", sbHolder4); + assertEquals(0, statusBarIconList.getViewIndex("bbb", sb4Tag)); + assertEquals(1, statusBarIconList.getViewIndex("bbb", sb3Tag)); + assertEquals(2, statusBarIconList.getViewIndex("bbb", TAG_PRIMARY)); + assertEquals(3, statusBarIconList.getViewIndex("ccc", TAG_PRIMARY)); } /** @@ -172,4 +205,4 @@ public class StatusBarIconListTest extends SysuiTestCase { return true; } -}
\ No newline at end of file +} 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 0c1d04253bf5..79fce82a1bcd 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 @@ -47,6 +47,7 @@ import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SysuiStatusBarStateController; @@ -275,21 +276,18 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { // Should be false to start, so no invocations mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, false /* animated */); - verify(mKeyguardUpdateMonitor, never()).onKeyguardOccludedChanged(anyBoolean()); verify(mKeyguardStateController, never()).notifyKeyguardState(anyBoolean(), anyBoolean()); clearInvocations(mKeyguardUpdateMonitor); clearInvocations(mKeyguardStateController); mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */); - verify(mKeyguardUpdateMonitor).onKeyguardOccludedChanged(true); verify(mKeyguardStateController).notifyKeyguardState(true, true); clearInvocations(mKeyguardUpdateMonitor); clearInvocations(mKeyguardStateController); mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */); - verify(mKeyguardUpdateMonitor, never()).onKeyguardOccludedChanged(anyBoolean()); verify(mKeyguardStateController, never()).notifyKeyguardState(anyBoolean(), anyBoolean()); } @@ -299,7 +297,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mStatusBarKeyguardViewManager.show(null); mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */); - verify(mKeyguardUpdateMonitor).onKeyguardOccludedChanged(true); verify(mKeyguardStateController).notifyKeyguardState(true, true); } @@ -309,7 +306,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mStatusBarKeyguardViewManager.show(null); mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */); - verify(mKeyguardUpdateMonitor).onKeyguardOccludedChanged(true); verify(mKeyguardStateController).notifyKeyguardState(true, true); } @@ -389,6 +385,16 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test + public void testBouncerIsOrWillBeShowing_whenBouncerIsInTransit() { + when(mBouncer.isShowing()).thenReturn(false); + when(mBouncer.inTransit()).thenReturn(true); + + assertTrue( + "Is or will be showing should be true when bouncer is in transit", + mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()); + } + + @Test public void testShowAltAuth_unlockingWithBiometricNotAllowed() { // GIVEN alt auth exists, unlocking with biometric isn't allowed mStatusBarKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index ecea14c6a522..7046150ba373 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -59,18 +59,17 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.statusbar.NotificationClickNotifier; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.NotifPipelineFlags; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; @@ -127,8 +126,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { @Mock private ShadeControllerImpl mShadeController; @Mock - private NotifPipelineFlags mNotifPipelineFlags; - @Mock private NotifPipeline mNotifPipeline; @Mock private NotificationVisibilityProvider mVisibilityProvider; @@ -148,15 +145,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private ActivityLaunchAnimator mActivityLaunchAnimator; @Mock private InteractionJankMonitor mJankMonitor; - private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); - private NotificationTestHelper mNotificationTestHelper; + private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); private ExpandableNotificationRow mNotificationRow; private ExpandableNotificationRow mBubbleNotificationRow; private final Answer<Void> mCallOnDismiss = answerVoid( (OnDismissAction dismissAction, Runnable cancel, Boolean afterKeyguardGone) -> dismissAction.onDismiss()); - private ArrayList<NotificationEntry> mActiveNotifications; @Before public void setUp() throws Exception { @@ -165,29 +160,28 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { when(mContentIntent.getCreatorUserHandle()).thenReturn(UserHandle.of(1)); when(mContentIntent.getIntent()).thenReturn(mContentIntentInner); - mNotificationTestHelper = new NotificationTestHelper( + NotificationTestHelper notificationTestHelper = new NotificationTestHelper( mContext, mDependency, TestableLooper.get(this)); // Create standard notification with contentIntent - mNotificationRow = mNotificationTestHelper.createRow(); + mNotificationRow = notificationTestHelper.createRow(); StatusBarNotification sbn = mNotificationRow.getEntry().getSbn(); sbn.getNotification().contentIntent = mContentIntent; sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL; // Create bubble notification row with contentIntent - mBubbleNotificationRow = mNotificationTestHelper.createBubble(); + mBubbleNotificationRow = notificationTestHelper.createBubble(); StatusBarNotification bubbleSbn = mBubbleNotificationRow.getEntry().getSbn(); bubbleSbn.getNotification().contentIntent = mContentIntent; bubbleSbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL; - mActiveNotifications = new ArrayList<>(); - mActiveNotifications.add(mNotificationRow.getEntry()); - mActiveNotifications.add(mBubbleNotificationRow.getEntry()); - when(mEntryManager.getVisibleNotifications()).thenReturn(mActiveNotifications); + ArrayList<NotificationEntry> activeNotifications = new ArrayList<>(); + activeNotifications.add(mNotificationRow.getEntry()); + activeNotifications.add(mBubbleNotificationRow.getEntry()); + when(mEntryManager.getVisibleNotifications()).thenReturn(activeNotifications); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); - when(mNotifPipelineFlags.isNewPipelineEnabled()).thenReturn(false); when(mOnUserInteractionCallback.registerFutureDismissal(eq(mNotificationRow.getEntry()), anyInt())).thenReturn(mFutureDismissalRunnable); when(mVisibilityProvider.obtain(anyString(), anyBoolean())) @@ -207,23 +201,19 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter = new StatusBarNotificationActivityStarter( getContext(), - mock(CommandQueue.class), mHandler, mUiBgExecutor, - mEntryManager, mNotifPipeline, mVisibilityProvider, headsUpManager, mActivityStarter, mClickNotifier, - mock(StatusBarStateController.class), mStatusBarKeyguardViewManager, mock(KeyguardManager.class), mock(IDreamManager.class), Optional.of(mBubblesManager), () -> mAssistManager, mRemoteInputManager, - mock(NotificationGroupManagerLegacy.class), mock(NotificationLockscreenUserManager.class), mShadeController, mKeyguardStateController, @@ -231,7 +221,6 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(LockPatternUtils.class), mock(StatusBarRemoteInputCallback.class), mActivityIntentHelper, - mNotifPipelineFlags, mock(MetricsLogger.class), mock(StatusBarNotificationActivityStarterLogger.class), mOnUserInteractionCallback, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java index 1a3dd3a7a2a5..4b5d1f747ca0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java @@ -33,12 +33,13 @@ import androidx.test.filters.SmallTest; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; -import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.ForegroundServiceNotificationListener; import com.android.systemui.InitController; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; +import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.LockscreenShadeTransitionController; @@ -50,7 +51,6 @@ import com.android.systemui.statusbar.NotificationViewHierarchyManager; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.notification.DynamicPrivacyController; import com.android.systemui.statusbar.notification.NotifPipelineFlags; -import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder; import com.android.systemui.statusbar.notification.collection.render.NotifShadeEventSource; @@ -61,7 +61,6 @@ import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -125,15 +124,12 @@ public class StatusBarNotificationPresenterTest extends SysuiTestCase { mock(NotificationLockscreenUserManager.class), mock(SysuiStatusBarStateController.class), mock(NotifShadeEventSource.class), - mock(NotificationEntryManager.class), mock(NotificationMediaManager.class), mock(NotificationGutsManager.class), - mock(KeyguardUpdateMonitor.class), lockscreenGestureLogger, mInitController, mNotificationInterruptStateProvider, mock(NotificationRemoteInputManager.class), - mock(ConfigurationController.class), mock(NotifPipelineFlags.class), mock(NotificationRemoteInputManager.Callback.class), mock(NotificationListContainer.class)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt index 011279721fd2..746c92e485b7 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt @@ -27,6 +27,7 @@ import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.shade.NotificationPanelViewController import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.StatusBarStateControllerImpl import com.android.systemui.statusbar.policy.KeyguardStateController @@ -39,12 +40,12 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito -import org.mockito.Mockito.`when` import org.mockito.Mockito.anyLong import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest @@ -184,4 +185,4 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { controller.startAnimation() assertFalse(controller.isAnimationPlaying()) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 6abc687f0ebb..56804d58d241 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -41,10 +41,13 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiBaseFragmentTest; +import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.LogcatEchoTracker; +import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationPanelViewController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.DisableFlagsLogger; import com.android.systemui.statusbar.OperatorNameViewController; @@ -52,7 +55,6 @@ import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler; import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; import com.android.systemui.statusbar.phone.NotificationIconAreaController; -import com.android.systemui.statusbar.phone.NotificationPanelViewController; import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarLocationPublisher; @@ -106,6 +108,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { private NotificationPanelViewController mNotificationPanelViewController; @Mock private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; + @Mock + private DumpManager mDumpManager; public CollapsedStatusBarFragmentTest() { super(CollapsedStatusBarFragment.class); @@ -114,6 +118,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { @Before public void setup() { injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); + mDependency.injectMockDependency(DarkIconDispatcher.class); } @Test @@ -122,7 +127,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, false); - assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility()); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); assertEquals(View.VISIBLE, getClockView().getVisibility()); } @@ -132,11 +137,11 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_SYSTEM_INFO, 0, false); - assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility()); + assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); - assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility()); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); } @Test @@ -232,7 +237,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, false); - assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility()); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); assertEquals(View.VISIBLE, getClockView().getVisibility()); } @@ -244,7 +249,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { fragment.disable(DEFAULT_DISPLAY, 0, 0, false); - assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility()); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); assertEquals(View.VISIBLE, getClockView().getVisibility()); } @@ -255,12 +260,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true); // Make sure they start out as visible - assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility()); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); assertEquals(View.VISIBLE, getClockView().getVisibility()); fragment.disable(DEFAULT_DISPLAY, 0, 0, false); - assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility()); + assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); assertEquals(View.GONE, getClockView().getVisibility()); } @@ -271,14 +276,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { when(mNotificationPanelViewController.hasCustomClock()).thenReturn(true); // Make sure they start out as visible - assertEquals(View.VISIBLE, getSystemIconAreaView().getVisibility()); + assertEquals(View.VISIBLE, getEndSideContentView().getVisibility()); assertEquals(View.VISIBLE, getClockView().getVisibility()); fragment.onDozingChanged(true); // When this callback is triggered, we want to make sure the clock and system info // visibilities are recalculated. Since dozing=true, they shouldn't be visible. - assertEquals(View.INVISIBLE, getSystemIconAreaView().getVisibility()); + assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility()); assertEquals(View.GONE, getClockView().getVisibility()); } @@ -384,7 +389,8 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { ), mOperatorNameViewControllerFactory, mSecureSettings, - mExecutor); + mExecutor, + mDumpManager); } private void setUpDaggerComponent() { @@ -417,7 +423,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { return mFragment.getView().findViewById(R.id.clock); } - private View getSystemIconAreaView() { - return mFragment.getView().findViewById(R.id.system_icon_area); + private View getEndSideContentView() { + return mFragment.getView().findViewById(R.id.status_bar_end_side_content); } } 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 e0bf9e7b0081..09d7c03c2091 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 @@ -44,6 +44,7 @@ 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.DialogCuj import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.broadcast.BroadcastSender @@ -53,7 +54,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.qs.QSUserSwitcherEvent import com.android.systemui.qs.user.UserSwitchDialogController import com.android.systemui.settings.UserTracker -import com.android.systemui.statusbar.phone.NotificationShadeWindowView +import com.android.systemui.shade.NotificationShadeWindowView import com.android.systemui.telephony.TelephonyListenerManager import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any @@ -72,12 +73,12 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock -import org.mockito.Mockito.`when` import org.mockito.Mockito.doNothing import org.mockito.Mockito.doReturn import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) @@ -362,7 +363,10 @@ class UserSwitcherControllerTest : SysuiTestCase() { userSwitcherController.onUserListItemClicked(currentGuestUserRecord, dialogShower) assertNotNull(userSwitcherController.mExitGuestDialog) testableLooper.processAllMessages() - verify(dialogShower).showDialog(userSwitcherController.mExitGuestDialog) + verify(dialogShower) + .showDialog( + userSwitcherController.mExitGuestDialog, + DialogCuj(InteractionJankMonitor.CUJ_USER_DIALOG_OPEN, "exit_guest_mode")) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt index eaad69c6b9d2..66367ecfc95c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt @@ -23,6 +23,7 @@ import android.view.LayoutInflater import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.classifier.FalsingCollector import com.android.systemui.plugins.FalsingManager import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.policy.UserSwitcherController @@ -46,6 +47,8 @@ class UserSwitcherActivityTest : SysuiTestCase() { @Mock private lateinit var layoutInflater: LayoutInflater @Mock + private lateinit var falsingCollector: FalsingCollector + @Mock private lateinit var falsingManager: FalsingManager @Mock private lateinit var userManager: UserManager @@ -59,6 +62,7 @@ class UserSwitcherActivityTest : SysuiTestCase() { userSwitcherController, broadcastDispatcher, layoutInflater, + falsingCollector, falsingManager, userManager, userTracker diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java index 22d7273dcd20..046ad1293521 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java @@ -145,4 +145,28 @@ public class ObservableServiceConnectionTest extends SysuiTestCase { connection.unbind(); verify(mContext, never()).unbindService(eq(connection)); } + + @Test + public void testUnbind() { + ObservableServiceConnection<Foo> connection = new ObservableServiceConnection<>(mContext, + mIntent, mExecutor, mTransformer); + connection.addCallback(mCallback); + connection.onServiceDisconnected(mComponentName); + + // Disconnects before binds should be ignored. + verify(mCallback, never()).onDisconnected(eq(connection), anyInt()); + + when(mContext.bindService(eq(mIntent), anyInt(), eq(mExecutor), eq(connection))) + .thenReturn(true); + connection.bind(); + + mExecutor.runAllReady(); + + connection.unbind(); + + mExecutor.runAllReady(); + + verify(mCallback).onDisconnected(eq(connection), + eq(ObservableServiceConnection.DISCONNECT_REASON_UNBIND)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java index 53d4a96b0640..db0139c9b0d1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/util/service/PersistentConnectionManagerTest.java @@ -16,6 +16,7 @@ package com.android.systemui.util.service; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.testing.AndroidTestingRunner; @@ -120,6 +121,24 @@ public class PersistentConnectionManagerTest extends SysuiTestCase { } /** + * Ensures manual unbind does not reconnect. + */ + @Test + public void testStopDoesNotReconnect() { + mConnectionManager.start(); + ArgumentCaptor<ObservableServiceConnection.Callback<Proxy>> connectionCallbackCaptor = + ArgumentCaptor.forClass(ObservableServiceConnection.Callback.class); + + verify(mConnection).addCallback(connectionCallbackCaptor.capture()); + verify(mConnection).bind(); + Mockito.clearInvocations(mConnection); + mConnectionManager.stop(); + mFakeExecutor.advanceClockToNext(); + mFakeExecutor.runAllReady(); + verify(mConnection, never()).bind(); + } + + /** * Ensures rebind on package change. */ @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index 2e58fc4f93fa..6a0124a3be8a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -71,6 +71,7 @@ import android.testing.TestableLooper; import android.util.Pair; import android.util.SparseArray; import android.view.View; +import android.view.ViewTreeObserver; import android.view.WindowManager; import androidx.test.filters.SmallTest; @@ -84,6 +85,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.model.SysUiState; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.NotificationShadeWindowView; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.RankingBuilder; @@ -104,7 +106,6 @@ import com.android.systemui.statusbar.notification.row.NotificationTestHelper; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; -import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.phone.ShadeController; import com.android.systemui.statusbar.policy.BatteryController; @@ -132,6 +133,7 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; +import com.android.wm.shell.sysui.ShellController; import org.junit.Before; import org.junit.Ignore; @@ -213,6 +215,8 @@ public class BubblesTest extends SysuiTestCase { private BubbleEntry mBubbleEntry2User11; @Mock + private ShellController mShellController; + @Mock private Bubbles.BubbleExpandListener mBubbleExpandListener; @Mock private PendingIntent mDeleteIntent; @@ -265,6 +269,8 @@ public class BubblesTest extends SysuiTestCase { ShellExecutor syncExecutor = new SyncExecutor(); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); + when(mNotificationShadeWindowView.getViewTreeObserver()) + .thenReturn(mock(ViewTreeObserver.class)); mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, @@ -325,6 +331,7 @@ public class BubblesTest extends SysuiTestCase { when(mShellTaskOrganizer.getExecutor()).thenReturn(syncExecutor); mBubbleController = new TestableBubbleController( mContext, + mShellController, mBubbleData, mFloatingContentCoordinator, mDataRepository, @@ -353,7 +360,6 @@ public class BubblesTest extends SysuiTestCase { mNotificationShadeWindowController, mock(KeyguardStateController.class), mShadeController, - mConfigurationController, mStatusBarService, mock(INotificationManager.class), mVisibilityProvider, @@ -375,6 +381,11 @@ public class BubblesTest extends SysuiTestCase { } @Test + public void instantiateController_registerConfigChangeListener() { + verify(mShellController, times(1)).addConfigurationChangeListener(any()); + } + + @Test public void testAddBubble() { mBubbleController.updateBubble(mBubbleEntry); assertTrue(mBubbleController.hasBubbles()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java index 17e5778f7aab..f901c327b76e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java @@ -38,6 +38,7 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.draganddrop.DragAndDropController; import com.android.wm.shell.onehanded.OneHandedController; +import com.android.wm.shell.sysui.ShellController; import java.util.Optional; @@ -48,6 +49,7 @@ public class TestableBubbleController extends BubbleController { // Let's assume surfaces can be synchronized immediately. TestableBubbleController(Context context, + ShellController shellController, BubbleData data, FloatingContentCoordinator floatingContentCoordinator, BubbleDataRepository dataRepository, @@ -67,11 +69,12 @@ public class TestableBubbleController extends BubbleController { Handler shellMainHandler, TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue) { - super(context, data, Runnable::run, floatingContentCoordinator, dataRepository, - statusBarService, windowManager, windowManagerShellWrapper, userManager, - launcherApps, bubbleLogger, taskStackListener, shellTaskOrganizer, positioner, - displayController, oneHandedOptional, dragAndDropController, shellMainExecutor, - shellMainHandler, new SyncExecutor(), taskViewTransitions, syncQueue); + super(context, shellController, data, Runnable::run, floatingContentCoordinator, + dataRepository, statusBarService, windowManager, windowManagerShellWrapper, + userManager, launcherApps, bubbleLogger, taskStackListener, shellTaskOrganizer, + positioner, displayController, oneHandedOptional, dragAndDropController, + shellMainExecutor, shellMainHandler, new SyncExecutor(), taskViewTransitions, + syncQueue); setInflateSynchronously(true); initialize(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 185942e6fbc8..72ade2632078 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -24,12 +24,10 @@ import android.test.suitebuilder.annotation.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.model.SysUiState; -import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -37,14 +35,12 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.ShellCommandHandler; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.compatui.CompatUI; -import com.android.wm.shell.draganddrop.DragAndDrop; -import com.android.wm.shell.hidedisplaycutout.HideDisplayCutout; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.splitscreen.SplitScreen; +import com.android.wm.shell.sysui.ShellInterface; import org.junit.Before; import org.junit.Test; @@ -65,35 +61,30 @@ import java.util.Optional; public class WMShellTest extends SysuiTestCase { WMShell mWMShell; + @Mock ShellInterface mShellInterface; @Mock CommandQueue mCommandQueue; @Mock ConfigurationController mConfigurationController; @Mock KeyguardStateController mKeyguardStateController; @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock NavigationModeController mNavigationModeController; @Mock ScreenLifecycle mScreenLifecycle; @Mock SysUiState mSysUiState; @Mock Pip mPip; @Mock SplitScreen mSplitScreen; @Mock OneHanded mOneHanded; - @Mock HideDisplayCutout mHideDisplayCutout; @Mock WakefulnessLifecycle mWakefulnessLifecycle; @Mock ProtoTracer mProtoTracer; @Mock ShellCommandHandler mShellCommandHandler; - @Mock CompatUI mCompatUI; @Mock UserInfoController mUserInfoController; @Mock ShellExecutor mSysUiMainExecutor; - @Mock DragAndDrop mDragAndDrop; @Before public void setUp() { MockitoAnnotations.initMocks(this); - mWMShell = new WMShell(mContext, Optional.of(mPip), - Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mHideDisplayCutout), - Optional.of(mShellCommandHandler), Optional.of(mCompatUI), - Optional.of(mDragAndDrop), - mCommandQueue, mConfigurationController, mKeyguardStateController, - mKeyguardUpdateMonitor, mNavigationModeController, mScreenLifecycle, mSysUiState, + mWMShell = new WMShell(mContext, mShellInterface, Optional.of(mPip), + Optional.of(mSplitScreen), Optional.of(mOneHanded), + Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController, + mKeyguardStateController, mKeyguardUpdateMonitor, mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle, mUserInfoController, mSysUiMainExecutor); } @@ -105,35 +96,12 @@ public class WMShellTest extends SysuiTestCase { } @Test - public void initSplitScreen_registersCallbacks() { - mWMShell.initSplitScreen(mSplitScreen); - - verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class)); - } - - @Test public void initOneHanded_registersCallbacks() { mWMShell.initOneHanded(mOneHanded); - verify(mKeyguardUpdateMonitor).registerCallback(any(KeyguardUpdateMonitorCallback.class)); verify(mCommandQueue).addCallback(any(CommandQueue.Callbacks.class)); verify(mScreenLifecycle).addObserver(any(ScreenLifecycle.Observer.class)); verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class)); verify(mOneHanded).registerEventCallback(any(OneHandedEventCallback.class)); } - - @Test - public void initHideDisplayCutout_registersCallbacks() { - mWMShell.initHideDisplayCutout(mHideDisplayCutout); - - verify(mConfigurationController).addCallback( - any(ConfigurationController.ConfigurationListener.class)); - } - - @Test - public void initCompatUI_registersCallbacks() { - mWMShell.initCompatUi(mCompatUI); - - verify(mKeyguardStateController).addCallback(any(KeyguardStateController.Callback.class)); - } } diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java index 1af8ad344190..84707a8d9c00 100644 --- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java +++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java @@ -398,18 +398,7 @@ public class AppPredictionPerUserService extends final IBinder.DeathRecipient mDeathRecipient; private final RemoteCallbackList<IPredictionCallback> mCallbacks = - new RemoteCallbackList<IPredictionCallback>() { - @Override - public void onCallbackDied(IPredictionCallback callback) { - if (DEBUG) { - Slog.d(TAG, "Binder died for session Id=" + mSessionId - + " and callback=" + callback.asBinder()); - } - if (mCallbacks.getRegisteredCallbackCount() == 0) { - destroy(); - } - } - }; + new RemoteCallbackList<>(); AppPredictionSessionInfo( @NonNull final AppPredictionSessionId id, diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 8fe57e18ea37..4892718d6203 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -123,6 +123,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.widget.IRemoteViewsFactory; import com.android.server.LocalServices; +import com.android.server.ServiceThread; import com.android.server.WidgetBackupProvider; import org.xmlpull.v1.XmlPullParser; @@ -266,7 +267,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku mDevicePolicyManagerInternal = LocalServices.getService(DevicePolicyManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mSaveStateHandler = BackgroundThread.getHandler(); - mCallbackHandler = new CallbackHandler(mContext.getMainLooper()); + final ServiceThread serviceThread = new ServiceThread(TAG, + android.os.Process.THREAD_PRIORITY_FOREGROUND, false /* allowIo */); + serviceThread.start(); + mCallbackHandler = new CallbackHandler(serviceThread.getLooper()); mBackupRestoreController = new BackupRestoreController(); mSecurityPolicy = new SecurityPolicy(); mIsProviderInfoPersisted = !ActivityManager.isLowRamDeviceStatic() @@ -307,26 +311,26 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); packageFilter.addDataScheme("package"); mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, - packageFilter, null, null); + packageFilter, null, mCallbackHandler); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, - sdFilter, null, null); + sdFilter, null, mCallbackHandler); IntentFilter offModeFilter = new IntentFilter(); offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE); offModeFilter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE); mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, - offModeFilter, null, null); + offModeFilter, null, mCallbackHandler); IntentFilter suspendPackageFilter = new IntentFilter(); suspendPackageFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED); suspendPackageFilter.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED); mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, - suspendPackageFilter, null, null); + suspendPackageFilter, null, mCallbackHandler); } private void registerOnCrossProfileProvidersChangedListener() { @@ -1218,11 +1222,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku try { // Ask ActivityManager to bind it. Notice that we are binding the service with the // caller app instead of DevicePolicyManagerService. - if(ActivityManager.getService().bindService( + if (ActivityManager.getService().bindService( caller, activtiyToken, intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), - connection, flags, mContext.getOpPackageName(), - widget.provider.getUserId()) != 0) { + connection, flags & (Context.BIND_AUTO_CREATE + | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE), + mContext.getOpPackageName(), widget.provider.getUserId()) != 0) { // Add it to the mapping of RemoteViewsService to appWidgetIds so that we // can determine when we can call back to the RemoteViewsService later to diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java index a2b20593a9cb..f523773033d1 100644 --- a/services/companion/java/com/android/server/companion/PackageUtils.java +++ b/services/companion/java/com/android/server/companion/PackageUtils.java @@ -30,6 +30,7 @@ import android.companion.CompanionDeviceService; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.pm.FeatureInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.PackageInfoFlags; @@ -39,8 +40,6 @@ import android.content.pm.ServiceInfo; import android.os.Binder; import android.util.Slog; -import com.android.internal.util.ArrayUtils; - import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -65,15 +64,20 @@ final class PackageUtils { static void enforceUsesCompanionDeviceFeature(@NonNull Context context, @UserIdInt int userId, @NonNull String packageName) { - final boolean requested = ArrayUtils.contains( - getPackageInfo(context, userId, packageName).reqFeatures, - FEATURE_COMPANION_DEVICE_SETUP); - - if (requested) { - throw new IllegalStateException("Must declare uses-feature " - + FEATURE_COMPANION_DEVICE_SETUP - + " in manifest to use this API"); + String requiredFeature = FEATURE_COMPANION_DEVICE_SETUP; + + FeatureInfo[] requestedFeatures = getPackageInfo(context, userId, packageName).reqFeatures; + if (requestedFeatures != null) { + for (int i = 0; i < requestedFeatures.length; i++) { + if (requiredFeature.equals(requestedFeatures[i].name)) { + return; + } + } } + + throw new IllegalStateException("Must declare uses-feature " + + requiredFeature + + " in manifest to use this API"); } /** diff --git a/services/contentcapture/Android.bp b/services/contentcapture/Android.bp index 434f239dbddc..5392c2cde3b8 100644 --- a/services/contentcapture/Android.bp +++ b/services/contentcapture/Android.bp @@ -17,6 +17,9 @@ filegroup { java_library_static { name: "services.contentcapture", defaults: ["platform_service_defaults"], - srcs: [":services.contentcapture-sources"], + srcs: [ + ":services.contentcapture-sources", + "java/**/*.logtags", + ], libs: ["services.core"], } diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java index 41a759254909..0428b2322f27 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java @@ -60,6 +60,7 @@ import android.service.contentcapture.SnapshotData; import android.service.voice.VoiceInteractionManagerInternal; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; @@ -88,6 +89,11 @@ final class ContentCapturePerUserService private static final String TAG = ContentCapturePerUserService.class.getSimpleName(); + private static final int EVENT_LOG_CONNECT_STATE_DIED = 0; + static final int EVENT_LOG_CONNECT_STATE_CONNECTED = 1; + static final int EVENT_LOG_CONNECT_STATE_DISCONNECTED = 2; + + @GuardedBy("mLock") private final SparseArray<ContentCaptureServerSession> mSessions = new SparseArray<>(); @@ -190,9 +196,13 @@ final class ContentCapturePerUserService Slog.w(TAG, "remote service died: " + service); synchronized (mLock) { mZombie = true; + ComponentName serviceComponent = getServiceComponentName(); writeServiceEvent( FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED, - getServiceComponentName()); + serviceComponent); + EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId, + serviceComponent != null ? serviceComponent.flattenToShortString() : "", + EVENT_LOG_CONNECT_STATE_DIED); } } @@ -614,11 +624,16 @@ final class ContentCapturePerUserService ? "null_activities" : activities.size() + " activities") + ")" + " for user " + mUserId); } + int packageCount = packages != null ? packages.size() : 0; + int activityCount = activities != null ? activities.size() : 0; ArraySet<String> oldList = mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId); + EventLog.writeEvent(EventLogTags.CC_CURRENT_ALLOWLIST, mUserId, oldList.size()); mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities); + EventLog.writeEvent(EventLogTags.CC_SET_ALLOWLIST, mUserId, + packageCount, activityCount); writeSetWhitelistEvent(getServiceComponentName(), packages, activities); updateContentCaptureOptions(oldList); @@ -699,12 +714,14 @@ final class ContentCapturePerUserService private void updateContentCaptureOptions(@Nullable ArraySet<String> oldList) { ArraySet<String> adding = mMaster.mGlobalContentCaptureOptions .getWhitelistedPackages(mUserId); + EventLog.writeEvent(EventLogTags.CC_CURRENT_ALLOWLIST, mUserId, adding.size()); if (oldList != null && adding != null) { adding.removeAll(oldList); } int N = adding != null ? adding.size() : 0; + EventLog.writeEvent(EventLogTags.CC_UPDATE_OPTIONS, mUserId, N); for (int i = 0; i < N; i++) { String packageName = adding.valueAt(i); ContentCaptureOptions options = mMaster.mGlobalContentCaptureOptions diff --git a/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags b/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags new file mode 100644 index 000000000000..6722b9ed3c5f --- /dev/null +++ b/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags @@ -0,0 +1,8 @@ +# See system/logging/logcat/event.logtags for a description of the format of this file. + +option java_package com.android.server.contentcapture + +53200 cc_connect_state_changed (User|1|5),(component|3),(type|1) +53201 cc_set_allowlist (User|1|5),(package_count|1),(activity_count|1) +53202 cc_current_allowlist (User|1|5),(count|1) +53203 cc_update_options (User|1|5),(count|1) diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java index 1efe55aa0767..e22a9d07b4ae 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java @@ -31,6 +31,7 @@ import android.service.contentcapture.IContentCaptureService; import android.service.contentcapture.IContentCaptureServiceCallback; import android.service.contentcapture.IDataShareCallback; import android.service.contentcapture.SnapshotData; +import android.util.EventLog; import android.util.Slog; import android.view.contentcapture.ContentCaptureContext; import android.view.contentcapture.DataRemovalRequest; @@ -47,6 +48,7 @@ final class RemoteContentCaptureService private final IBinder mServerCallback; private final int mIdleUnbindTimeoutMs; private final ContentCapturePerUserService mPerUserService; + private final int mUserId; RemoteContentCaptureService(Context context, String serviceInterface, ComponentName serviceComponentName, IContentCaptureServiceCallback callback, int userId, @@ -61,6 +63,7 @@ final class RemoteContentCaptureService mPerUserService = perUserService; mServerCallback = callback.asBinder(); mIdleUnbindTimeoutMs = idleUnbindTimeoutMs; + mUserId = userId; // Bind right away, which will trigger a onConnected() on service's ensureBoundLocked(); @@ -88,6 +91,9 @@ final class RemoteContentCaptureService writeServiceEvent( FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED, mComponentName); + EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId, + mComponentName != null ? mComponentName.flattenToShortString() : "", + ContentCapturePerUserService.EVENT_LOG_CONNECT_STATE_CONNECTED); } finally { // Update the system-service state, in case the service reconnected after // dying @@ -98,6 +104,9 @@ final class RemoteContentCaptureService writeServiceEvent( FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED, mComponentName); + EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId, + mComponentName != null ? mComponentName.flattenToShortString() : "", + ContentCapturePerUserService.EVENT_LOG_CONNECT_STATE_DISCONNECTED); } } catch (Exception e) { Slog.w(mTag, "Exception calling onConnectedStateChanged(" + connected + "): " + e); diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index c678a67e5bd3..e1a0bfd25c9f 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -160,6 +160,7 @@ public class Watchdog implements Dumpable { public static final String[] AIDL_INTERFACE_PREFIXES_OF_INTEREST = new String[] { "android.hardware.biometrics.face.IFace/", "android.hardware.biometrics.fingerprint.IFingerprint/", + "android.hardware.input.processor.IInputProcessor/", "android.hardware.light.ILights/", "android.hardware.power.IPower/", "android.hardware.power.stats.IPowerStats/", diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 00d728ba0fef..07a5fb5d0f0f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -14389,6 +14389,19 @@ public class ActivityManagerService extends IActivityManager.Stub final int callingPid = Binder.getCallingPid(); final int callingUid = Binder.getCallingUid(); + // Non-system callers can't declare that a broadcast is alarm-related. + // The PendingIntent invocation case is handled in PendingIntentRecord. + if (bOptions != null && callingUid != SYSTEM_UID) { + if (bOptions.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)) { + if (DEBUG_BROADCAST) { + Slog.w(TAG, "Non-system caller " + callingUid + + " may not flag broadcast as alarm-related"); + } + throw new SecurityException( + "Non-system callers may not flag broadcasts as alarm-related"); + } + } + final long origId = Binder.clearCallingIdentity(); try { return broadcastIntentLocked(callerApp, @@ -14402,6 +14415,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } + // Not the binder call surface int broadcastIntentInPackage(String packageName, @Nullable String featureId, int uid, int realCallingUid, int realCallingPid, Intent intent, String resolvedType, IIntentReceiver resultTo, int resultCode, String resultData, Bundle resultExtras, diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 19ffc1733f3d..ae91d75ef0ce 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -70,6 +70,7 @@ final class BroadcastRecord extends Binder { final boolean callerInstantApp; // caller is an Instant App? final boolean ordered; // serialize the send to receivers? final boolean sticky; // originated from existing sticky data? + final boolean alarm; // originated from an alarm triggering? final boolean initialSticky; // initial broadcast from register to sticky? final int userId; // user id this broadcast was for final String resolvedType; // the resolved data type @@ -305,6 +306,7 @@ final class BroadcastRecord extends Binder { this.allowBackgroundActivityStarts = allowBackgroundActivityStarts; mBackgroundActivityStartsToken = backgroundActivityStartsToken; this.timeoutExempt = timeoutExempt; + alarm = options != null && options.isAlarmBroadcast(); } /** @@ -357,6 +359,7 @@ final class BroadcastRecord extends Binder { allowBackgroundActivityStarts = from.allowBackgroundActivityStarts; mBackgroundActivityStartsToken = from.mBackgroundActivityStartsToken; timeoutExempt = from.timeoutExempt; + alarm = from.alarm; } /** diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index a61cbbfbfc91..e9658dbc7b1a 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1482,7 +1482,9 @@ public class OomAdjuster { if (!cycleReEval) { // Don't reset this flag when doing cycles re-evaluation. state.setNoKillOnBgRestrictedAndIdle(false); - app.mOptRecord.setShouldNotFreeze(false); + // If this UID is currently allowlisted, it should not be frozen. + final UidRecord uidRec = app.getUidRecord(); + app.mOptRecord.setShouldNotFreeze(uidRec != null && uidRec.isCurAllowListed()); } final int appUid = app.info.uid; diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 4044cceb606b..bda60ff2172b 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -18,6 +18,7 @@ package com.android.server.am; import static android.app.ActivityManager.START_SUCCESS; +import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST_LIGHT; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; @@ -34,6 +35,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.PowerWhitelistManager; import android.os.PowerWhitelistManager.ReasonCode; +import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.TransactionTooLargeException; @@ -416,6 +418,22 @@ public final class PendingIntentRecord extends IIntentSender.Stub { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); + + // Only system senders can declare a broadcast to be alarm-originated. We check + // this here rather than in the general case handling below to fail before the other + // invocation side effects such as allowlisting. + if (options != null && callingUid != Process.SYSTEM_UID + && key.type == ActivityManager.INTENT_SENDER_BROADCAST) { + if (options.containsKey(BroadcastOptions.KEY_ALARM_BROADCAST)) { + if (DEBUG_BROADCAST_LIGHT) { + Slog.w(TAG, "Non-system caller " + callingUid + + " may not flag broadcast as alarm-related"); + } + throw new SecurityException( + "Non-system callers may not flag broadcasts as alarm-related"); + } + } + final long origId = Binder.clearCallingIdentity(); int res = START_SUCCESS; diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 92a8dcd2ba8f..98e3a214435a 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2269,7 +2269,9 @@ public final class ProcessList { final boolean inBgRestricted = ast.isAppBackgroundRestricted( app.info.uid, app.info.packageName); if (inBgRestricted) { - mAppsInBackgroundRestricted.add(app); + synchronized (mService) { + mAppsInBackgroundRestricted.add(app); + } } app.mState.setBackgroundRestricted(inBgRestricted); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 0040ea9215b3..3a869f859e52 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -9181,11 +9181,8 @@ public class AudioService extends IAudioService.Stub Settings.Secure.SPATIAL_AUDIO_ENABLED, UserHandle.USER_CURRENT); if (settings == null) { Log.e(TAG, "error reading spatial audio device settings"); - } else { - Log.v(TAG, "restoring spatial audio device settings: " + settings); - mSpatializerHelper.setSADeviceSettings(settings); } - mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect); + mSpatializerHelper.init(/*effectExpected*/ mHasSpatializerEffect, settings); mSpatializerHelper.setFeatureEnabled(mHasSpatializerEffect); } @@ -10373,6 +10370,11 @@ public class AudioService extends IAudioService.Stub @Override public void setAccessibilityServiceUids(IntArray uids) { + // TODO(b/233287010): Fix voice interaction and a11y concurrency in audio policy service + if (isPlatformAutomotive()) { + return; + } + synchronized (mAccessibilityServiceUidsLock) { if (uids.size() == 0) { mAccessibilityServiceUids = null; diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java index dd44af1b68ee..cd5960ffbf32 100644 --- a/services/core/java/com/android/server/audio/SpatializerHelper.java +++ b/services/core/java/com/android/server/audio/SpatializerHelper.java @@ -175,7 +175,7 @@ public class SpatializerHelper { mASA = asa; } - synchronized void init(boolean effectExpected) { + synchronized void init(boolean effectExpected, @Nullable String settings) { loglogi("init effectExpected=" + effectExpected); if (!effectExpected) { loglogi("init(): setting state to STATE_NOT_SUPPORTED due to effect not expected"); @@ -278,6 +278,13 @@ public class SpatializerHelper { mSACapableDeviceTypes.add(SPAT_MODE_FOR_DEVICE_TYPE.keyAt(i)); } } + + // When initialized from AudioService, the settings string will be non-null. + // Saved settings need to be applied after spatialization support is initialized above. + if (settings != null) { + setSADeviceSettings(settings); + } + // for both transaural / binaural, we are not forcing enablement as the init() method // could have been called another time after boot in case of audioserver restart addCompatibleAudioDevice( @@ -316,7 +323,7 @@ public class SpatializerHelper { mState = STATE_UNINITIALIZED; mSpatLevel = Spatializer.SPATIALIZER_IMMERSIVE_LEVEL_NONE; mActualHeadTrackingMode = Spatializer.HEAD_TRACKING_MODE_UNSUPPORTED; - init(true); + init(true, null /* settings */); setSpatializerEnabledInt(featureEnabled); } @@ -731,7 +738,7 @@ public class SpatializerHelper { return; } if (mState == STATE_UNINITIALIZED) { - init(true); + init(true, null /* settings */); } setSpatializerEnabledInt(true); } else { diff --git a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java index ad24cf0591ca..262be08b60e2 100644 --- a/services/core/java/com/android/server/biometrics/log/BiometricLogger.java +++ b/services/core/java/com/android/server/biometrics/log/BiometricLogger.java @@ -28,8 +28,10 @@ import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.OperationContext; import android.hardware.face.FaceManager; import android.hardware.fingerprint.FingerprintManager; +import android.util.Log; import android.util.Slog; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.Utils; @@ -41,6 +43,10 @@ public class BiometricLogger { public static final String TAG = "BiometricLogger"; public static final boolean DEBUG = false; + private static final Object sLock = new Object(); + + @GuardedBy("sLock") + private static int sAlsCounter; private final int mStatsModality; private final int mStatsAction; @@ -345,13 +351,33 @@ public class BiometricLogger { if (!mLightSensorEnabled) { mLightSensorEnabled = true; mLastAmbientLux = 0; - mSensorManager.registerListener(mLightSensorListener, lightSensor, - SensorManager.SENSOR_DELAY_NORMAL); + int localAlsCounter; + synchronized (sLock) { + localAlsCounter = sAlsCounter++; + } + + if (localAlsCounter == 0) { + mSensorManager.registerListener(mLightSensorListener, lightSensor, + SensorManager.SENSOR_DELAY_NORMAL); + } else { + Slog.e(TAG, "Ignoring request to subscribe to ALSProbe due to non-zero ALS" + + " counter: " + localAlsCounter); + Slog.e(TAG, Log.getStackTraceString(new Throwable())); + } } } else { mLightSensorEnabled = false; mLastAmbientLux = 0; mSensorManager.unregisterListener(mLightSensorListener); + int localAlsCounter; + synchronized (sLock) { + localAlsCounter = --sAlsCounter; + } + if (localAlsCounter != 0) { + Slog.e(TAG, "Non-zero ALS counter after unsubscribing from ALSProbe: " + + localAlsCounter); + Slog.e(TAG, Log.getStackTraceString(new Throwable())); + } } } } diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 61350bb6095e..076ac2b4bfe7 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -215,9 +215,12 @@ public class ClipboardService extends SystemService { private class ListenerInfo { final int mUid; final String mPackageName; - ListenerInfo(int uid, String packageName) { + final String mAttributionTag; + + ListenerInfo(int uid, String packageName, String attributionTag) { mUid = uid; mPackageName = packageName; + mAttributionTag = attributionTag; } } @@ -355,27 +358,43 @@ public class ClipboardService extends SystemService { } @Override - public void setPrimaryClip(ClipData clip, String callingPackage, @UserIdInt int userId) { - checkAndSetPrimaryClip(clip, callingPackage, userId, callingPackage); + public void setPrimaryClip( + ClipData clip, + String callingPackage, + String attributionTag, + @UserIdInt int userId) { + checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, callingPackage); } @Override public void setPrimaryClipAsPackage( - ClipData clip, String callingPackage, @UserIdInt int userId, String sourcePackage) { + ClipData clip, + String callingPackage, + String attributionTag, + @UserIdInt int userId, + String sourcePackage) { getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE, "Requires SET_CLIP_SOURCE permission"); - checkAndSetPrimaryClip(clip, callingPackage, userId, sourcePackage); + checkAndSetPrimaryClip(clip, callingPackage, attributionTag, userId, sourcePackage); } private void checkAndSetPrimaryClip( - ClipData clip, String callingPackage, @UserIdInt int userId, String sourcePackage) { + ClipData clip, + String callingPackage, + String attributionTag, + @UserIdInt int userId, + String sourcePackage) { if (clip == null || clip.getItemCount() <= 0) { throw new IllegalArgumentException("No items"); } final int intendingUid = getIntendingUid(callingPackage, userId); final int intendingUserId = UserHandle.getUserId(intendingUid); - if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage, - intendingUid, intendingUserId)) { + if (!clipboardAccessAllowed( + AppOpsManager.OP_WRITE_CLIPBOARD, + callingPackage, + attributionTag, + intendingUid, + intendingUserId)) { return; } checkDataOwner(clip, intendingUid); @@ -392,8 +411,13 @@ public class ClipboardService extends SystemService { PROPERTY_AUTO_CLEAR_ENABLED, true)) { mClipboardClearHandler.removeEqualMessages(ClipboardClearHandler.MSG_CLEAR, userId); - Message clearMessage = Message.obtain(mClipboardClearHandler, - ClipboardClearHandler.MSG_CLEAR, userId, intendingUid, userId); + Message clearMessage = + Message.obtain( + mClipboardClearHandler, + ClipboardClearHandler.MSG_CLEAR, + userId, + intendingUid, + userId); mClipboardClearHandler.sendMessageDelayed(clearMessage, getTimeoutForAutoClear()); } @@ -409,11 +433,16 @@ public class ClipboardService extends SystemService { } @Override - public void clearPrimaryClip(String callingPackage, @UserIdInt int userId) { + public void clearPrimaryClip( + String callingPackage, String attributionTag, @UserIdInt int userId) { final int intendingUid = getIntendingUid(callingPackage, userId); final int intendingUserId = UserHandle.getUserId(intendingUid); - if (!clipboardAccessAllowed(AppOpsManager.OP_WRITE_CLIPBOARD, callingPackage, - intendingUid, intendingUserId)) { + if (!clipboardAccessAllowed( + AppOpsManager.OP_WRITE_CLIPBOARD, + callingPackage, + attributionTag, + intendingUid, + intendingUserId)) { return; } synchronized (mLock) { @@ -424,11 +453,15 @@ public class ClipboardService extends SystemService { } @Override - public ClipData getPrimaryClip(String pkg, @UserIdInt int userId) { + public ClipData getPrimaryClip(String pkg, String attributionTag, @UserIdInt int userId) { final int intendingUid = getIntendingUid(pkg, userId); final int intendingUserId = UserHandle.getUserId(intendingUid); - if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, pkg, - intendingUid, intendingUserId) + if (!clipboardAccessAllowed( + AppOpsManager.OP_READ_CLIPBOARD, + pkg, + attributionTag, + intendingUid, + intendingUserId) || isDeviceLocked(intendingUserId)) { return null; } @@ -453,12 +486,17 @@ public class ClipboardService extends SystemService { } @Override - public ClipDescription getPrimaryClipDescription(String callingPackage, - @UserIdInt int userId) { + public ClipDescription getPrimaryClipDescription( + String callingPackage, String attributionTag, @UserIdInt int userId) { final int intendingUid = getIntendingUid(callingPackage, userId); final int intendingUserId = UserHandle.getUserId(intendingUid); - if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage, - intendingUid, intendingUserId, false) + if (!clipboardAccessAllowed( + AppOpsManager.OP_READ_CLIPBOARD, + callingPackage, + attributionTag, + intendingUid, + intendingUserId, + false) || isDeviceLocked(intendingUserId)) { return null; } @@ -470,11 +508,17 @@ public class ClipboardService extends SystemService { } @Override - public boolean hasPrimaryClip(String callingPackage, @UserIdInt int userId) { + public boolean hasPrimaryClip( + String callingPackage, String attributionTag, @UserIdInt int userId) { final int intendingUid = getIntendingUid(callingPackage, userId); final int intendingUserId = UserHandle.getUserId(intendingUid); - if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage, - intendingUid, intendingUserId, false) + if (!clipboardAccessAllowed( + AppOpsManager.OP_READ_CLIPBOARD, + callingPackage, + attributionTag, + intendingUid, + intendingUserId, + false) || isDeviceLocked(intendingUserId)) { return false; } @@ -484,19 +528,28 @@ public class ClipboardService extends SystemService { } @Override - public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener, - String callingPackage, @UserIdInt int userId) { + public void addPrimaryClipChangedListener( + IOnPrimaryClipChangedListener listener, + String callingPackage, + String attributionTag, + @UserIdInt int userId) { final int intendingUid = getIntendingUid(callingPackage, userId); final int intendingUserId = UserHandle.getUserId(intendingUid); synchronized (mLock) { - getClipboardLocked(intendingUserId).primaryClipListeners.register(listener, - new ListenerInfo(intendingUid, callingPackage)); + getClipboardLocked(intendingUserId) + .primaryClipListeners + .register( + listener, + new ListenerInfo(intendingUid, callingPackage, attributionTag)); } } @Override - public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener, - String callingPackage, @UserIdInt int userId) { + public void removePrimaryClipChangedListener( + IOnPrimaryClipChangedListener listener, + String callingPackage, + String attributionTag, + @UserIdInt int userId) { final int intendingUserId = getIntendingUserId(callingPackage, userId); synchronized (mLock) { getClipboardLocked(intendingUserId).primaryClipListeners.unregister(listener); @@ -504,11 +557,16 @@ public class ClipboardService extends SystemService { } @Override - public boolean hasClipboardText(String callingPackage, int userId) { + public boolean hasClipboardText(String callingPackage, String attributionTag, int userId) { final int intendingUid = getIntendingUid(callingPackage, userId); final int intendingUserId = UserHandle.getUserId(intendingUid); - if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage, - intendingUid, intendingUserId, false) + if (!clipboardAccessAllowed( + AppOpsManager.OP_READ_CLIPBOARD, + callingPackage, + attributionTag, + intendingUid, + intendingUserId, + false) || isDeviceLocked(intendingUserId)) { return false; } @@ -523,13 +581,19 @@ public class ClipboardService extends SystemService { } @Override - public String getPrimaryClipSource(String callingPackage, int userId) { + public String getPrimaryClipSource( + String callingPackage, String attributionTag, int userId) { getContext().enforceCallingOrSelfPermission(Manifest.permission.SET_CLIP_SOURCE, "Requires SET_CLIP_SOURCE permission"); final int intendingUid = getIntendingUid(callingPackage, userId); final int intendingUserId = UserHandle.getUserId(intendingUid); - if (!clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, callingPackage, - intendingUid, intendingUserId, false) + if (!clipboardAccessAllowed( + AppOpsManager.OP_READ_CLIPBOARD, + callingPackage, + attributionTag, + intendingUid, + intendingUserId, + false) || isDeviceLocked(intendingUserId)) { return null; } @@ -718,8 +782,12 @@ public class ClipboardService extends SystemService { ListenerInfo li = (ListenerInfo) clipboard.primaryClipListeners.getBroadcastCookie(i); - if (clipboardAccessAllowed(AppOpsManager.OP_READ_CLIPBOARD, li.mPackageName, - li.mUid, UserHandle.getUserId(li.mUid))) { + if (clipboardAccessAllowed( + AppOpsManager.OP_READ_CLIPBOARD, + li.mPackageName, + li.mAttributionTag, + li.mUid, + UserHandle.getUserId(li.mUid))) { clipboard.primaryClipListeners.getBroadcastItem(i) .dispatchPrimaryClipChanged(); } @@ -965,13 +1033,18 @@ public class ClipboardService extends SystemService { } } - private boolean clipboardAccessAllowed(int op, String callingPackage, int uid, - @UserIdInt int userId) { - return clipboardAccessAllowed(op, callingPackage, uid, userId, true); + private boolean clipboardAccessAllowed( + int op, String callingPackage, String attributionTag, int uid, @UserIdInt int userId) { + return clipboardAccessAllowed(op, callingPackage, attributionTag, uid, userId, true); } - private boolean clipboardAccessAllowed(int op, String callingPackage, int uid, - @UserIdInt int userId, boolean shouldNoteOp) { + private boolean clipboardAccessAllowed( + int op, + String callingPackage, + String attributionTag, + int uid, + @UserIdInt int userId, + boolean shouldNoteOp) { boolean allowed; @@ -1040,7 +1113,7 @@ public class ClipboardService extends SystemService { // Finally, check the app op. int appOpsResult; if (shouldNoteOp) { - appOpsResult = mAppOps.noteOp(op, uid, callingPackage); + appOpsResult = mAppOps.noteOp(op, uid, callingPackage, attributionTag, null); } else { appOpsResult = mAppOps.checkOp(op, uid, callingPackage); } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 90b9967eef59..d14902eaf8f5 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -522,10 +522,12 @@ final class LogicalDisplay { // Set the layer stack. device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack); // Also inform whether the device is the same one sent to inputflinger for its layerstack. + // Prevent displays that are disabled from receiving input. // TODO(b/188914255): Remove once input can dispatch against device vs layerstack. device.setDisplayFlagsLocked(t, - device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE - ? SurfaceControl.DISPLAY_RECEIVES_INPUT : 0); + (isEnabled() && device.getDisplayDeviceInfoLocked().touch != TOUCH_NONE) + ? SurfaceControl.DISPLAY_RECEIVES_INPUT + : 0); // Set the color mode and allowed display mode. if (device == mPrimaryDisplayDevice) { diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index 63c5456c972b..7b60345caf87 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -23,6 +23,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static com.android.server.wm.ActivityInterceptorCallback.DREAM_MANAGER_ORDERED_ID; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.TaskInfo; @@ -45,6 +46,9 @@ import android.os.IBinder; import android.os.Looper; import android.os.PowerManager; import android.os.PowerManagerInternal; +import android.os.RemoteException; +import android.os.ResultReceiver; +import android.os.ShellCallback; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; @@ -221,6 +225,10 @@ public final class DreamManagerService extends SystemService { } } + protected void requestStartDreamFromShell() { + requestDreamInternal(); + } + private void requestDreamInternal() { // Ask the power manager to nap. It will eventually call back into // startDream() if/when it is appropriate to start dreaming. @@ -275,6 +283,10 @@ public final class DreamManagerService extends SystemService { } } + protected void requestStopDreamFromShell() { + stopDreamInternal(true, "stopping dream from shell"); + } + private void stopDreamInternal(boolean immediate, String reason) { synchronized (mLock) { stopDreamLocked(immediate, reason); @@ -593,6 +605,14 @@ public final class DreamManagerService extends SystemService { } } + public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out, + @Nullable FileDescriptor err, + @NonNull String[] args, @Nullable ShellCallback callback, + @NonNull ResultReceiver resultReceiver) throws RemoteException { + new DreamShellCommand(DreamManagerService.this, mPowerManager) + .exec(this, in, out, err, args, callback, resultReceiver); + } + @Override // Binder call public ComponentName[] getDreamComponents() { return getDreamComponentsForUser(UserHandle.getCallingUserId()); diff --git a/services/core/java/com/android/server/dreams/DreamShellCommand.java b/services/core/java/com/android/server/dreams/DreamShellCommand.java new file mode 100644 index 000000000000..eae7e80a89f1 --- /dev/null +++ b/services/core/java/com/android/server/dreams/DreamShellCommand.java @@ -0,0 +1,92 @@ +/* + * 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.dreams; + +import android.annotation.NonNull; +import android.os.Binder; +import android.os.PowerManager; +import android.os.Process; +import android.os.ShellCommand; +import android.os.SystemClock; +import android.text.TextUtils; +import android.util.Slog; + +import java.io.PrintWriter; + +/** + * {@link DreamShellCommand} allows accessing dream functionality, including toggling dream state. + */ +public class DreamShellCommand extends ShellCommand { + private static final boolean DEBUG = true; + private static final String TAG = "DreamShellCommand"; + private final @NonNull DreamManagerService mService; + private final @NonNull PowerManager mPowerManager; + + DreamShellCommand(@NonNull DreamManagerService service, @NonNull PowerManager powerManager) { + mService = service; + mPowerManager = powerManager; + } + + @Override + public int onCommand(String cmd) { + final int callingUid = Binder.getCallingUid(); + if (callingUid != Process.ROOT_UID) { + Slog.e(TAG, "Must be root before calling Dream shell commands"); + return -1; + } + + if (TextUtils.isEmpty(cmd)) { + return super.handleDefaultCommands(cmd); + } + if (DEBUG) { + Slog.d(TAG, "onCommand:" + cmd); + } + + switch (cmd) { + case "start-dreaming": + return startDreaming(); + case "stop-dreaming": + return stopDreaming(); + default: + return super.handleDefaultCommands(cmd); + } + } + + private int startDreaming() { + mPowerManager.wakeUp(SystemClock.uptimeMillis(), + PowerManager.WAKE_REASON_PLUGGED_IN, "shell:cmd:android.service.dreams:DREAM"); + mService.requestStartDreamFromShell(); + return 0; + } + + private int stopDreaming() { + mService.requestStopDreamFromShell(); + return 0; + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + pw.println("Dream manager (dreams) commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" start-dreaming"); + pw.println(" Start the currently configured dream."); + pw.println(" stop-dreaming"); + pw.println(" Stops any active dream"); + } +} diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java index e222c644da9e..d238dae634ad 100644 --- a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java +++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java @@ -27,8 +27,6 @@ import android.view.InputWindowHandle; import android.view.SurfaceControl; import android.view.WindowManager; -import com.android.server.policy.WindowManagerPolicy; - /** * An internal implementation of an {@link InputMonitor} that uses a spy window. * @@ -69,9 +67,7 @@ class GestureMonitorSpyWindow { final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); t.setInputWindowInfo(mInputSurface, mWindowHandle); - // Gesture monitor should be above handwriting event surface, hence setting it to - // WindowManagerPolicy.INPUT_DISPLAY_OVERLAY_LAYER + 1 - t.setLayer(mInputSurface, WindowManagerPolicy.INPUT_DISPLAY_OVERLAY_LAYER + 1); + t.setLayer(mInputSurface, Integer.MAX_VALUE); t.setPosition(mInputSurface, 0, 0); t.setCrop(mInputSurface, null /* crop to parent surface */); t.show(mInputSurface); diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java index 5438faa8793e..8180e66166d9 100644 --- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java +++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java @@ -27,8 +27,6 @@ import android.view.InputWindowHandle; import android.view.SurfaceControl; import android.view.WindowManager; -import com.android.server.policy.WindowManagerPolicy; - final class HandwritingEventReceiverSurface { public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName(); @@ -38,8 +36,7 @@ final class HandwritingEventReceiverSurface { // is above gesture monitors, then edge-back and swipe-up gestures won't work when this surface // is intercepting. // TODO(b/217538817): Specify the ordering in WM by usage. - private static final int HANDWRITING_SURFACE_LAYER = - WindowManagerPolicy.INPUT_DISPLAY_OVERLAY_LAYER; + private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE - 1; private final InputWindowHandle mWindowHandle; private final InputChannel mClientChannel; diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 1a1c265f568d..9d15ed33797f 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -5754,10 +5754,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub @Override public void onImeParentChanged() { synchronized (ImfLock.class) { - // Hide the IME method menu when the IME surface parent will change in - // case seeing the dialog dismiss flickering during the next focused window - // starting the input connection. - mMenuController.hideInputMethodMenu(); + // Hide the IME method menu only when the IME surface parent is changed by the + // input target changed, in case seeing the dialog dismiss flickering during + // the next focused window starting the input connection. + if (mLastImeTargetWindow != mCurFocusedWindow) { + mMenuController.hideInputMethodMenu(); + } } } diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java index 111621da06ce..5b2188ac078e 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java @@ -1183,11 +1183,11 @@ public class ContextHubService extends IContextHubService.Stub { } } else { Log.d(TAG, "BT adapter not available. Defaulting to disabled"); - if (mIsBtMainEnabled) { + if (forceUpdate || mIsBtMainEnabled) { mIsBtMainEnabled = false; mContextHubWrapper.onBtMainSettingChanged(mIsBtMainEnabled); } - if (mIsBtScanningEnabled) { + if (forceUpdate || mIsBtScanningEnabled) { mIsBtScanningEnabled = false; mContextHubWrapper.onBtScanningSettingChanged(mIsBtScanningEnabled); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index fdb31b2e9bd8..e589080786b5 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1794,6 +1794,7 @@ public class NotificationManagerService extends SystemService { if (userHandle >= 0) { cancelAllNotificationsInt(MY_UID, MY_PID, null, null, 0, 0, true, userHandle, REASON_PROFILE_TURNED_OFF, null); + mSnoozeHelper.clearData(userHandle); } } else if (action.equals(Intent.ACTION_USER_PRESENT)) { // turn off LED when user passes through lock screen @@ -2458,11 +2459,11 @@ public class NotificationManagerService extends SystemService { SnoozeHelper snoozeHelper = new SnoozeHelper(getContext(), (userId, r, muteOnReturn) -> { try { if (DBG) { - Slog.d(TAG, "Reposting " + r.getKey()); + Slog.d(TAG, "Reposting " + r.getKey() + " " + muteOnReturn); } enqueueNotificationInternal(r.getSbn().getPackageName(), r.getSbn().getOpPkg(), r.getSbn().getUid(), r.getSbn().getInitialPid(), r.getSbn().getTag(), - r.getSbn().getId(), r.getSbn().getNotification(), userId, true); + r.getSbn().getId(), r.getSbn().getNotification(), userId, muteOnReturn); } catch (Exception e) { Slog.e(TAG, "Cannot un-snooze notification", e); } @@ -7030,6 +7031,7 @@ public class NotificationManagerService extends SystemService { @GuardedBy("mNotificationLock") void snoozeLocked(NotificationRecord r) { + final List<NotificationRecord> recordsToSnooze = new ArrayList<>(); if (r.getSbn().isGroup()) { final List<NotificationRecord> groupNotifications = findCurrentAndSnoozedGroupNotificationsLocked( @@ -7038,8 +7040,8 @@ public class NotificationManagerService extends SystemService { if (r.getNotification().isGroupSummary()) { // snooze all children for (int i = 0; i < groupNotifications.size(); i++) { - if (mKey != groupNotifications.get(i).getKey()) { - snoozeNotificationLocked(groupNotifications.get(i)); + if (!mKey.equals(groupNotifications.get(i).getKey())) { + recordsToSnooze.add(groupNotifications.get(i)); } } } else { @@ -7049,8 +7051,8 @@ public class NotificationManagerService extends SystemService { if (groupNotifications.size() == 2) { // snooze summary and the one child for (int i = 0; i < groupNotifications.size(); i++) { - if (mKey != groupNotifications.get(i).getKey()) { - snoozeNotificationLocked(groupNotifications.get(i)); + if (!mKey.equals(groupNotifications.get(i).getKey())) { + recordsToSnooze.add(groupNotifications.get(i)); } } } @@ -7058,7 +7060,15 @@ public class NotificationManagerService extends SystemService { } } // snooze the notification - snoozeNotificationLocked(r); + recordsToSnooze.add(r); + + if (mSnoozeHelper.canSnooze(recordsToSnooze.size())) { + for (int i = 0; i < recordsToSnooze.size(); i++) { + snoozeNotificationLocked(recordsToSnooze.get(i)); + } + } else { + Log.w(TAG, "Cannot snooze " + r.getKey() + ": too many snoozed notifications"); + } } diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index 09ed56745e54..12324bff0c7a 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -16,6 +16,8 @@ package com.android.server.notification; +import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; +import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.PERMISSION_GRANTED; @@ -175,12 +177,14 @@ public final class PermissionHelper { mPermManager.revokeRuntimePermission(packageName, NOTIFICATION_PERMISSION, userId, TAG); } + int flagMask = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED; + flagMask = userSet || !grant ? flagMask | FLAG_PERMISSION_GRANTED_BY_DEFAULT : flagMask; if (userSet) { mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, userId); + flagMask, FLAG_PERMISSION_USER_SET, true, userId); } else { mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, - 0, FLAG_PERMISSION_USER_SET, true, userId); + flagMask, 0, true, userId); } } catch (RemoteException e) { Slog.e(TAG, "Could not reach system server", e); diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java index 7f265df3f416..61936df4a124 100644 --- a/services/core/java/com/android/server/notification/SnoozeHelper.java +++ b/services/core/java/com/android/server/notification/SnoozeHelper.java @@ -16,7 +16,6 @@ package com.android.server.notification; import android.annotation.NonNull; -import android.annotation.UserIdInt; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -25,7 +24,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.Binder; -import android.os.SystemClock; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.util.ArrayMap; @@ -38,18 +36,15 @@ import android.util.TypedXmlSerializer; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; -import com.android.internal.util.XmlUtils; import com.android.server.pm.PackageManagerService; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; @@ -59,15 +54,15 @@ import java.util.Set; /** * NotificationManagerService helper for handling snoozed notifications. */ -public class SnoozeHelper { +public final class SnoozeHelper { public static final int XML_SNOOZED_NOTIFICATION_VERSION = 1; + static final int CONCURRENT_SNOOZE_LIMIT = 500; + protected static final String XML_TAG_NAME = "snoozed-notifications"; private static final String XML_SNOOZED_NOTIFICATION = "notification"; private static final String XML_SNOOZED_NOTIFICATION_CONTEXT = "context"; - private static final String XML_SNOOZED_NOTIFICATION_PKG = "pkg"; - private static final String XML_SNOOZED_NOTIFICATION_USER_ID = "user-id"; private static final String XML_SNOOZED_NOTIFICATION_KEY = "key"; //the time the snoozed notification should be reposted private static final String XML_SNOOZED_NOTIFICATION_TIME = "time"; @@ -89,24 +84,19 @@ public class SnoozeHelper { private AlarmManager mAm; private final ManagedServices.UserProfiles mUserProfiles; - // User id | package name : notification key : record. - private ArrayMap<String, ArrayMap<String, NotificationRecord>> - mSnoozedNotifications = new ArrayMap<>(); - // User id | package name : notification key : time-milliseconds . + // notification key : record. + private ArrayMap<String, NotificationRecord> mSnoozedNotifications = new ArrayMap<>(); + // notification key : time-milliseconds . // This member stores persisted snoozed notification trigger times. it persists through reboots // It should have the notifications that haven't expired or re-posted yet - private final ArrayMap<String, ArrayMap<String, Long>> - mPersistedSnoozedNotifications = new ArrayMap<>(); - // User id | package name : notification key : creation ID . + private final ArrayMap<String, Long> mPersistedSnoozedNotifications = new ArrayMap<>(); + // notification key : creation ID. // This member stores persisted snoozed notification trigger context for the assistant // it persists through reboots. // It should have the notifications that haven't expired or re-posted yet - private final ArrayMap<String, ArrayMap<String, String>> + private final ArrayMap<String, String> mPersistedSnoozedNotificationsWithContext = new ArrayMap<>(); - // notification key : package. - private ArrayMap<String, String> mPackages = new ArrayMap<>(); - // key : userId - private ArrayMap<String, Integer> mUsers = new ArrayMap<>(); + private Callback mCallback; private final Object mLock = new Object(); @@ -123,27 +113,20 @@ public class SnoozeHelper { mUserProfiles = userProfiles; } - private String getPkgKey(@UserIdInt int userId, String pkg) { - return userId + "|" + pkg; - } - - void cleanupPersistedContext(String key){ + protected boolean canSnooze(int numberToSnooze) { synchronized (mLock) { - int userId = mUsers.get(key); - String pkg = mPackages.get(key); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext); + if ((mSnoozedNotifications.size() + numberToSnooze) > CONCURRENT_SNOOZE_LIMIT) { + return false; + } } + return true; } @NonNull protected Long getSnoozeTimeForUnpostedNotification(int userId, String pkg, String key) { Long time = null; synchronized (mLock) { - ArrayMap<String, Long> snoozed = - mPersistedSnoozedNotifications.get(getPkgKey(userId, pkg)); - if (snoozed != null) { - time = snoozed.get(key); - } + time = mPersistedSnoozedNotifications.get(key); } if (time == null) { time = 0L; @@ -153,29 +136,26 @@ public class SnoozeHelper { protected String getSnoozeContextForUnpostedNotification(int userId, String pkg, String key) { synchronized (mLock) { - ArrayMap<String, String> snoozed = - mPersistedSnoozedNotificationsWithContext.get(getPkgKey(userId, pkg)); - if (snoozed != null) { - return snoozed.get(key); - } + return mPersistedSnoozedNotificationsWithContext.get(key); } - return null; } protected boolean isSnoozed(int userId, String pkg, String key) { synchronized (mLock) { - return mSnoozedNotifications.containsKey(getPkgKey(userId, pkg)) - && mSnoozedNotifications.get(getPkgKey(userId, pkg)).containsKey(key); + return mSnoozedNotifications.containsKey(key); } } protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) { synchronized (mLock) { - if (mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))) { - return mSnoozedNotifications.get(getPkgKey(userId, pkg)).values(); + ArrayList snoozed = new ArrayList(); + for (NotificationRecord r : mSnoozedNotifications.values()) { + if (r.getUserId() == userId && r.getSbn().getPackageName().equals(pkg)) { + snoozed.add(r); + } } + return snoozed; } - return Collections.EMPTY_LIST; } @NonNull @@ -183,15 +163,11 @@ public class SnoozeHelper { String groupKey, Integer userId) { ArrayList<NotificationRecord> records = new ArrayList<>(); synchronized (mLock) { - ArrayMap<String, NotificationRecord> allRecords = - mSnoozedNotifications.get(getPkgKey(userId, pkg)); - if (allRecords != null) { - for (int i = 0; i < allRecords.size(); i++) { - NotificationRecord r = allRecords.valueAt(i); - String currentGroupKey = r.getSbn().getGroup(); - if (Objects.equals(currentGroupKey, groupKey)) { - records.add(r); - } + for (int i = 0; i < mSnoozedNotifications.size(); i++) { + NotificationRecord r = mSnoozedNotifications.valueAt(i); + if (r.getSbn().getPackageName().equals(pkg) && r.getUserId() == userId + && Objects.equals(r.getSbn().getGroup(), groupKey)) { + records.add(r); } } } @@ -200,31 +176,16 @@ public class SnoozeHelper { protected NotificationRecord getNotification(String key) { synchronized (mLock) { - if (!mUsers.containsKey(key) || !mPackages.containsKey(key)) { - Slog.w(TAG, "Snoozed data sets no longer agree for " + key); - return null; - } - int userId = mUsers.get(key); - String pkg = mPackages.get(key); - ArrayMap<String, NotificationRecord> snoozed = - mSnoozedNotifications.get(getPkgKey(userId, pkg)); - if (snoozed == null) { - return null; - } - return snoozed.get(key); + return mSnoozedNotifications.get(key); } } protected @NonNull List<NotificationRecord> getSnoozed() { synchronized (mLock) { - // caller filters records based on the current user profiles and listener access, so just - // return everything + // caller filters records based on the current user profiles and listener access, + // so just return everything List<NotificationRecord> snoozed = new ArrayList<>(); - for (String userPkgKey : mSnoozedNotifications.keySet()) { - ArrayMap<String, NotificationRecord> snoozedRecords = - mSnoozedNotifications.get(userPkgKey); - snoozed.addAll(snoozedRecords.values()); - } + snoozed.addAll(mSnoozedNotifications.values()); return snoozed; } } @@ -233,15 +194,13 @@ public class SnoozeHelper { * Snoozes a notification and schedules an alarm to repost at that time. */ protected void snooze(NotificationRecord record, long duration) { - String pkg = record.getSbn().getPackageName(); String key = record.getKey(); - int userId = record.getUser().getIdentifier(); snooze(record); - scheduleRepost(pkg, key, userId, duration); + scheduleRepost(key, duration); Long activateAt = System.currentTimeMillis() + duration; synchronized (mLock) { - storeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications, activateAt); + mPersistedSnoozedNotifications.put(key, activateAt); } } @@ -249,66 +208,33 @@ public class SnoozeHelper { * Records a snoozed notification. */ protected void snooze(NotificationRecord record, String contextId) { - int userId = record.getUser().getIdentifier(); if (contextId != null) { synchronized (mLock) { - storeRecordLocked(record.getSbn().getPackageName(), record.getKey(), - userId, mPersistedSnoozedNotificationsWithContext, contextId); + mPersistedSnoozedNotificationsWithContext.put(record.getKey(), contextId); } } snooze(record); } private void snooze(NotificationRecord record) { - int userId = record.getUser().getIdentifier(); if (DEBUG) { Slog.d(TAG, "Snoozing " + record.getKey()); } synchronized (mLock) { - storeRecordLocked(record.getSbn().getPackageName(), record.getKey(), - userId, mSnoozedNotifications, record); - } - } - - private <T> void storeRecordLocked(String pkg, String key, Integer userId, - ArrayMap<String, ArrayMap<String, T>> targets, T object) { - - mPackages.put(key, pkg); - mUsers.put(key, userId); - ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg)); - if (keyToValue == null) { - keyToValue = new ArrayMap<>(); - } - keyToValue.put(key, object); - targets.put(getPkgKey(userId, pkg), keyToValue); - } - - private <T> T removeRecordLocked(String pkg, String key, Integer userId, - ArrayMap<String, ArrayMap<String, T>> targets) { - T object = null; - ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg)); - if (keyToValue == null) { - return null; - } - object = keyToValue.remove(key); - if (keyToValue.size() == 0) { - targets.remove(getPkgKey(userId, pkg)); + mSnoozedNotifications.put(record.getKey(), record); } - return object; } protected boolean cancel(int userId, String pkg, String tag, int id) { synchronized (mLock) { - ArrayMap<String, NotificationRecord> recordsForPkg = - mSnoozedNotifications.get(getPkgKey(userId, pkg)); - if (recordsForPkg != null) { - final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet(); - for (Map.Entry<String, NotificationRecord> record : records) { - final StatusBarNotification sbn = record.getValue().getSbn(); - if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) { - record.getValue().isCanceled = true; - return true; - } + final Set<Map.Entry<String, NotificationRecord>> records = + mSnoozedNotifications.entrySet(); + for (Map.Entry<String, NotificationRecord> record : records) { + final StatusBarNotification sbn = record.getValue().getSbn(); + if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId + && Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) { + record.getValue().isCanceled = true; + return true; } } } @@ -325,11 +251,9 @@ public class SnoozeHelper { if (includeCurrentProfiles) { userIds = mUserProfiles.getCurrentProfileIds(); } - for (ArrayMap<String, NotificationRecord> snoozedRecords : mSnoozedNotifications.values()) { - for (NotificationRecord r : snoozedRecords.values()) { - if (userIds.binarySearch(r.getUserId()) >= 0) { - r.isCanceled = true; - } + for (NotificationRecord r : mSnoozedNotifications.values()) { + if (userIds.binarySearch(r.getUserId()) >= 0) { + r.isCanceled = true; } } } @@ -337,14 +261,12 @@ public class SnoozeHelper { protected boolean cancel(int userId, String pkg) { synchronized (mLock) { - ArrayMap<String, NotificationRecord> records = - mSnoozedNotifications.get(getPkgKey(userId, pkg)); - if (records == null) { - return false; - } - int N = records.size(); - for (int i = 0; i < N; i++) { - records.valueAt(i).isCanceled = true; + int n = mSnoozedNotifications.size(); + for (int i = 0; i < n; i++) { + final NotificationRecord r = mSnoozedNotifications.valueAt(i); + if (r.getSbn().getPackageName().equals(pkg) && r.getUserId() == userId) { + r.isCanceled = true; + } } return true; } @@ -355,20 +277,17 @@ public class SnoozeHelper { */ protected void update(int userId, NotificationRecord record) { synchronized (mLock) { - ArrayMap<String, NotificationRecord> records = - mSnoozedNotifications.get(getPkgKey(userId, record.getSbn().getPackageName())); - if (records == null) { - return; + if (mSnoozedNotifications.containsKey(record.getKey())) { + mSnoozedNotifications.put(record.getKey(), record); } - records.put(record.getKey(), record); } } protected void repost(String key, boolean muteOnReturn) { synchronized (mLock) { - Integer userId = mUsers.get(key); - if (userId != null) { - repost(key, userId, muteOnReturn); + final NotificationRecord r = mSnoozedNotifications.get(key); + if (r != null) { + repost(key, r.getUserId(), muteOnReturn); } } } @@ -376,43 +295,30 @@ public class SnoozeHelper { protected void repost(String key, int userId, boolean muteOnReturn) { NotificationRecord record; synchronized (mLock) { - final String pkg = mPackages.remove(key); - mUsers.remove(key); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotifications); - removeRecordLocked(pkg, key, userId, mPersistedSnoozedNotificationsWithContext); - ArrayMap<String, NotificationRecord> records = - mSnoozedNotifications.get(getPkgKey(userId, pkg)); - if (records == null) { - return; - } - record = records.remove(key); - + mPersistedSnoozedNotifications.remove(key); + mPersistedSnoozedNotificationsWithContext.remove(key); + record = mSnoozedNotifications.remove(key); } if (record != null && !record.isCanceled) { - final PendingIntent pi = createPendingIntent( - record.getSbn().getPackageName(), record.getKey(), userId); + final PendingIntent pi = createPendingIntent(record.getKey()); mAm.cancel(pi); MetricsLogger.action(record.getLogMaker() .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsProto.MetricsEvent.TYPE_OPEN)); - mCallback.repost(userId, record, muteOnReturn); + mCallback.repost(record.getUserId(), record, muteOnReturn); } } protected void repostGroupSummary(String pkg, int userId, String groupKey) { synchronized (mLock) { - ArrayMap<String, NotificationRecord> recordsByKey - = mSnoozedNotifications.get(getPkgKey(userId, pkg)); - if (recordsByKey == null) { - return; - } - String groupSummaryKey = null; - int N = recordsByKey.size(); - for (int i = 0; i < N; i++) { - final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i); - if (potentialGroupSummary.getSbn().isGroup() + int n = mSnoozedNotifications.size(); + for (int i = 0; i < n; i++) { + final NotificationRecord potentialGroupSummary = mSnoozedNotifications.valueAt(i); + if (potentialGroupSummary.getSbn().getPackageName().equals(pkg) + && potentialGroupSummary.getUserId() == userId + && potentialGroupSummary.getSbn().isGroup() && potentialGroupSummary.getNotification().isGroupSummary() && groupKey.equals(potentialGroupSummary.getGroupKey())) { groupSummaryKey = potentialGroupSummary.getKey(); @@ -421,16 +327,14 @@ public class SnoozeHelper { } if (groupSummaryKey != null) { - NotificationRecord record = recordsByKey.remove(groupSummaryKey); - mPackages.remove(groupSummaryKey); - mUsers.remove(groupSummaryKey); + NotificationRecord record = mSnoozedNotifications.remove(groupSummaryKey); if (record != null && !record.isCanceled) { Runnable runnable = () -> { MetricsLogger.action(record.getLogMaker() .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsProto.MetricsEvent.TYPE_OPEN)); - mCallback.repost(userId, record, false); + mCallback.repost(record.getUserId(), record, false); }; runnable.run(); } @@ -440,20 +344,40 @@ public class SnoozeHelper { protected void clearData(int userId, String pkg) { synchronized (mLock) { - ArrayMap<String, NotificationRecord> records = - mSnoozedNotifications.get(getPkgKey(userId, pkg)); - if (records == null) { - return; + int n = mSnoozedNotifications.size(); + for (int i = n - 1; i >= 0; i--) { + final NotificationRecord record = mSnoozedNotifications.valueAt(i); + if (record.getUserId() == userId && record.getSbn().getPackageName().equals(pkg)) { + mSnoozedNotifications.removeAt(i); + mPersistedSnoozedNotificationsWithContext.remove(record.getKey()); + mPersistedSnoozedNotifications.remove(record.getKey()); + Runnable runnable = () -> { + final PendingIntent pi = createPendingIntent(record.getKey()); + mAm.cancel(pi); + MetricsLogger.action(record.getLogMaker() + .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) + .setType(MetricsProto.MetricsEvent.TYPE_DISMISS)); + }; + runnable.run(); + } } - for (int i = records.size() - 1; i >= 0; i--) { - final NotificationRecord r = records.removeAt(i); - if (r != null) { - mPackages.remove(r.getKey()); - mUsers.remove(r.getKey()); + } + } + + protected void clearData(int userId) { + synchronized (mLock) { + int n = mSnoozedNotifications.size(); + for (int i = n - 1; i >= 0; i--) { + final NotificationRecord record = mSnoozedNotifications.valueAt(i); + if (record.getUserId() == userId) { + mSnoozedNotifications.removeAt(i); + mPersistedSnoozedNotificationsWithContext.remove(record.getKey()); + mPersistedSnoozedNotifications.remove(record.getKey()); + Runnable runnable = () -> { - final PendingIntent pi = createPendingIntent(pkg, r.getKey(), userId); + final PendingIntent pi = createPendingIntent(record.getKey()); mAm.cancel(pi); - MetricsLogger.action(r.getLogMaker() + MetricsLogger.action(record.getLogMaker() .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED) .setType(MetricsProto.MetricsEvent.TYPE_DISMISS)); }; @@ -463,47 +387,38 @@ public class SnoozeHelper { } } - private PendingIntent createPendingIntent(String pkg, String key, int userId) { + private PendingIntent createPendingIntent(String key) { return PendingIntent.getBroadcast(mContext, REQUEST_CODE_REPOST, new Intent(REPOST_ACTION) .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME) .setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build()) .addFlags(Intent.FLAG_RECEIVER_FOREGROUND) - .putExtra(EXTRA_KEY, key) - .putExtra(EXTRA_USER_ID, userId), + .putExtra(EXTRA_KEY, key), PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } public void scheduleRepostsForPersistedNotifications(long currentTime) { synchronized (mLock) { - for (ArrayMap<String, Long> snoozed : mPersistedSnoozedNotifications.values()) { - for (int i = 0; i < snoozed.size(); i++) { - String key = snoozed.keyAt(i); - Long time = snoozed.valueAt(i); - String pkg = mPackages.get(key); - Integer userId = mUsers.get(key); - if (time == null || pkg == null || userId == null) { - Slog.w(TAG, "data out of sync: " + time + "|" + pkg + "|" + userId); - continue; - } - if (time != null && time > currentTime) { - scheduleRepostAtTime(pkg, key, userId, time); - } + for (int i = 0; i < mPersistedSnoozedNotifications.size(); i++) { + String key = mPersistedSnoozedNotifications.keyAt(i); + Long time = mPersistedSnoozedNotifications.valueAt(i); + if (time != null && time > currentTime) { + scheduleRepostAtTime(key, time); } } } } - private void scheduleRepost(String pkg, String key, int userId, long duration) { - scheduleRepostAtTime(pkg, key, userId, System.currentTimeMillis() + duration); + private void scheduleRepost(String key, long duration) { + scheduleRepostAtTime(key, System.currentTimeMillis() + duration); } - private void scheduleRepostAtTime(String pkg, String key, int userId, long time) { + private void scheduleRepostAtTime(String key, long time) { Runnable runnable = () -> { final long identity = Binder.clearCallingIdentity(); try { - final PendingIntent pi = createPendingIntent(pkg, key, userId); + final PendingIntent pi = createPendingIntent(key); mAm.cancel(pi); if (DEBUG) Slog.d(TAG, "Scheduling evaluate for " + new Date(time)); mAm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pi); @@ -517,37 +432,14 @@ public class SnoozeHelper { public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) { synchronized (mLock) { pw.println("\n Snoozed notifications:"); - for (String userPkgKey : mSnoozedNotifications.keySet()) { + for (String key : mSnoozedNotifications.keySet()) { pw.print(INDENT); - pw.println("key: " + userPkgKey); - ArrayMap<String, NotificationRecord> snoozedRecords = - mSnoozedNotifications.get(userPkgKey); - Set<String> snoozedKeys = snoozedRecords.keySet(); - for (String key : snoozedKeys) { - pw.print(INDENT); - pw.print(INDENT); - pw.print(INDENT); - pw.println(key); - } + pw.println("key: " + key); } pw.println("\n Pending snoozed notifications"); - for (String userPkgKey : mPersistedSnoozedNotifications.keySet()) { + for (String key : mPersistedSnoozedNotifications.keySet()) { pw.print(INDENT); - pw.println("key: " + userPkgKey); - ArrayMap<String, Long> snoozedRecords = - mPersistedSnoozedNotifications.get(userPkgKey); - if (snoozedRecords == null) { - continue; - } - Set<String> snoozedKeys = snoozedRecords.keySet(); - for (String key : snoozedKeys) { - pw.print(INDENT); - pw.print(INDENT); - pw.print(INDENT); - pw.print(key); - pw.print(INDENT); - pw.println(snoozedRecords.get(key)); - } + pw.println("key: " + key + " until: " + mPersistedSnoozedNotifications.get(key)); } } } @@ -578,37 +470,22 @@ public class SnoozeHelper { void insert(T t) throws IOException; } - private <T> void writeXml(TypedXmlSerializer out, - ArrayMap<String, ArrayMap<String, T>> targets, String tag, - Inserter<T> attributeInserter) - throws IOException { - final int M = targets.size(); - for (int i = 0; i < M; i++) { + private <T> void writeXml(TypedXmlSerializer out, ArrayMap<String, T> targets, String tag, + Inserter<T> attributeInserter) throws IOException { + for (int j = 0; j < targets.size(); j++) { + String key = targets.keyAt(j); // T is a String (snoozed until context) or Long (snoozed until time) - ArrayMap<String, T> keyToValue = targets.valueAt(i); - for (int j = 0; j < keyToValue.size(); j++) { - String key = keyToValue.keyAt(j); - T value = keyToValue.valueAt(j); - String pkg = mPackages.get(key); - Integer userId = mUsers.get(key); - - if (pkg == null || userId == null) { - Slog.w(TAG, "pkg " + pkg + " or user " + userId + " missing for " + key); - continue; - } + T value = targets.valueAt(j); - out.startTag(null, tag); + out.startTag(null, tag); - attributeInserter.insert(value); + attributeInserter.insert(value); - out.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, - XML_SNOOZED_NOTIFICATION_VERSION); - out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key); - out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg); - out.attributeInt(null, XML_SNOOZED_NOTIFICATION_USER_ID, userId); + out.attributeInt(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL, + XML_SNOOZED_NOTIFICATION_VERSION); + out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key); - out.endTag(null, tag); - } + out.endTag(null, tag); } } @@ -628,16 +505,12 @@ public class SnoozeHelper { == XML_SNOOZED_NOTIFICATION_VERSION) { try { final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY); - final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG); - final int userId = parser.getAttributeInt( - null, XML_SNOOZED_NOTIFICATION_USER_ID, UserHandle.USER_ALL); if (tag.equals(XML_SNOOZED_NOTIFICATION)) { final Long time = parser.getAttributeLong( null, XML_SNOOZED_NOTIFICATION_TIME, 0); if (time > currentTime) { //only read new stuff synchronized (mLock) { - storeRecordLocked( - pkg, key, userId, mPersistedSnoozedNotifications, time); + mPersistedSnoozedNotifications.put(key, time); } } } @@ -645,9 +518,7 @@ public class SnoozeHelper { final String creationId = parser.getAttributeValue( null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID); synchronized (mLock) { - storeRecordLocked( - pkg, key, userId, mPersistedSnoozedNotificationsWithContext, - creationId); + mPersistedSnoozedNotificationsWithContext.put(key, creationId); } } } catch (Exception e) { diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index e90a5db39d71..7da5f51bcbc2 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -3701,8 +3701,9 @@ final class InstallPackageHelper { parsedPackage.getPackageName()); boolean ignoreSharedUserId = false; - if (installedPkgSetting == null) { - // We can directly ignore sharedUserSetting for new installs + if (installedPkgSetting == null || !installedPkgSetting.hasSharedUser()) { + // Directly ignore sharedUserSetting for new installs, or if the app has + // already left shared UID ignoreSharedUserId = parsedPackage.isLeavingSharedUid(); } diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java index 0dc188b75d5e..46f0dbcdfe10 100644 --- a/services/core/java/com/android/server/pm/ScanPackageUtils.java +++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java @@ -427,8 +427,7 @@ final class ScanPackageUtils { pkgSetting.setLastModifiedTime(scanFileTime); // TODO(b/135203078): Remove, move to constructor pkgSetting.setPkg(parsedPackage) - .setFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting)) - .setPrivateFlags( + .setPkgFlags(PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting), PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting)); if (parsedPackage.getLongVersionCode() != pkgSetting.getVersionCode()) { pkgSetting.setLongVersionCode(parsedPackage.getLongVersionCode()); diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java index b952f80850bb..61a251e19db7 100644 --- a/services/core/java/com/android/server/pm/SettingBase.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -146,6 +146,17 @@ public abstract class SettingBase implements Watchable, Snappable { return this; } + /** + * Unconditionally set both mPkgFlags and mPkgPrivateFlags. + * Should not be used outside pkgSetting initialization or update. + */ + SettingBase setPkgFlags(int flags, int privateFlags) { + this.mPkgFlags = flags; + this.mPkgPrivateFlags = privateFlags; + onChanged(); + return this; + } + public int getFlags() { return mPkgFlags; } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 6400502f1a89..7437b145189f 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -987,8 +987,7 @@ public final class Settings implements Watchable, Snappable { // Update new package state. .setLastModifiedTime(codePath.lastModified()) .setDomainSetId(domainSetId); - pkgSetting.setFlags(pkgFlags) - .setPrivateFlags(pkgPrivateFlags); + pkgSetting.setPkgFlags(pkgFlags, pkgPrivateFlags); } else { pkgSetting = new PackageSetting(pkgName, realPkgName, codePath, legacyNativeLibraryPath, primaryCpuAbi, secondaryCpuAbi, @@ -1175,15 +1174,15 @@ public final class Settings implements Watchable, Snappable { .setUsesStaticLibrariesVersions(null); } - // These two flags are preserved from the existing PackageSetting. Copied from prior code, - // unclear if this is actually necessary. - boolean wasExternalStorage = (pkgSetting.getFlags() - & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; - if (wasExternalStorage) { - pkgFlags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE; - } else { - pkgFlags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE; - } + // If what we are scanning is a system (and possibly privileged) package, + // then make it so, regardless of whether it was previously installed only + // in the data partition. Reset first. + int newPkgFlags = pkgSetting.getFlags(); + newPkgFlags &= ~ApplicationInfo.FLAG_SYSTEM; + newPkgFlags |= pkgFlags & ApplicationInfo.FLAG_SYSTEM; + // Only set pkgFlags. + pkgSetting.setPkgFlags(newPkgFlags, pkgSetting.getPrivateFlags()); + boolean wasRequiredForSystemUser = (pkgSetting.getPrivateFlags() & ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER) != 0; if (wasRequiredForSystemUser) { @@ -1191,9 +1190,7 @@ public final class Settings implements Watchable, Snappable { } else { pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER; } - - pkgSetting.setFlags(pkgFlags) - .setPrivateFlags(pkgPrivateFlags); + pkgSetting.setPrivateFlags(pkgPrivateFlags); } /** diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java index 281e1bd2824d..1366de77af8b 100644 --- a/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java +++ b/services/core/java/com/android/server/pm/pkg/component/ParsedPermissionUtils.java @@ -313,8 +313,7 @@ public class ParsedPermissionUtils { final ParsedPermission perm = checkDuplicatePerm.get(name); if (isMalformedDuplicate(parsedPermission, perm)) { // Fix for b/213323615 - EventLog.writeEvent(0x534e4554, "213323615", - "The package " + pkg.getPackageName() + " seems malicious"); + EventLog.writeEvent(0x534e4554, "213323615"); return true; } checkDuplicatePerm.put(name, parsedPermission); diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 9bc0553a7612..e8a3dcd5635f 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -155,10 +155,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { int FINISH_LAYOUT_REDO_ANIM = 0x0008; /** Layer for the screen off animation */ int COLOR_FADE_LAYER = 0x40000001; - /** Layer for Input overlays for capturing inputs for gesture detection, etc. */ - int INPUT_DISPLAY_OVERLAY_LAYER = 0x7f000000; - /** Layer for Screen Decoration: The top most visible layer just below input overlay layers */ - int SCREEN_DECOR_DISPLAY_OVERLAY_LAYER = INPUT_DISPLAY_OVERLAY_LAYER - 1; /** * Register shortcuts for window manager to dispatch. diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index 1ccdaf75821f..d2a00af245f6 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -577,6 +577,21 @@ class ActivityClientController extends IActivityClientController.Stub { } } + /** + * Returns the windowing mode of the task that hosts the activity, or {@code -1} if task is not + * found. + */ + @Override + public int getTaskWindowingMode(IBinder activityToken) { + synchronized (mGlobalLock) { + final ActivityRecord ar = ActivityRecord.isInAnyTask(activityToken); + if (ar == null) { + return -1; + } + return ar.getTask().getWindowingMode(); + } + } + @Override @Nullable public IBinder getActivityTokenBelow(IBinder activityToken) { diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index 270891fcf421..891654979965 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -149,6 +149,12 @@ class ActivityMetricsLogger { private static final long UNKNOWN_VISIBILITY_CHECK_DELAY_MS = 3000; /** + * If the recents animation is finished before the delay since the window drawn, do not log the + * action because the duration is too small that may be just an accidentally touch. + */ + private static final long LATENCY_TRACKER_RECENTS_DELAY_MS = 300; + + /** * The flag for {@link #notifyActivityLaunching} to skip associating a new launch with an active * transition, in the case the launch is standalone (e.g. from recents). */ @@ -738,10 +744,6 @@ class ActivityMetricsLogger { if (info.mLoggedTransitionStarting) { done(false /* abort */, info, "notifyWindowsDrawn", timestampNs); } - if (r.mWmService.isRecentsAnimationTarget(r)) { - r.mWmService.getRecentsAnimationController().logRecentsAnimationStartTime( - info.mSourceEventDelayMs + info.mWindowsDrawnDelayMs); - } return infoSnapshot; } @@ -786,12 +788,6 @@ 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); } } @@ -959,6 +955,9 @@ class ActivityMetricsLogger { launchObserverNotifyActivityLaunchFinished(info, timestampNs); } logAppTransitionFinished(info, isHibernating != null ? isHibernating : false); + if (info.mReason == APP_TRANSITION_RECENTS_ANIM) { + logRecentsAnimationLatency(info); + } } mTransitionInfoList.remove(info); } @@ -1114,6 +1113,22 @@ class ActivityMetricsLogger { Log.i(TAG, sb.toString()); } + private void logRecentsAnimationLatency(TransitionInfo info) { + final int duration = info.mSourceEventDelayMs + info.mWindowsDrawnDelayMs; + final ActivityRecord r = info.mLastLaunchedActivity; + final long lastTopLossTime = r.topResumedStateLossTime; + final WindowManagerService wm = mSupervisor.mService.mWindowManager; + final Object controller = wm.getRecentsAnimationController(); + mLoggerHandler.postDelayed(() -> { + if (lastTopLossTime != r.topResumedStateLossTime + || controller != wm.getRecentsAnimationController()) { + // Skip if the animation was finished in a short time. + return; + } + wm.mLatencyTracker.logAction(LatencyTracker.ACTION_START_RECENTS_ANIMATION, duration); + }, LATENCY_TRACKER_RECENTS_DELAY_MS); + } + private static int getAppStartTransitionType(int tronType, boolean relaunched) { if (tronType == TYPE_TRANSITION_COLD_LAUNCH) { return FrameworkStatsLog.APP_START_OCCURRED__TYPE__COLD; diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index a9b154c869e8..0a58044d66c6 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -224,6 +224,7 @@ import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WIND import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL; import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES; +import static com.android.server.wm.WindowManagerService.sEnableShellTransitions; import static com.android.server.wm.WindowState.LEGACY_POLICY_VISIBILITY; import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN; @@ -1992,6 +1993,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (options.getLaunchIntoPipParams() != null) { pictureInPictureArgs = options.getLaunchIntoPipParams(); + if (sourceRecord != null) { + adjustPictureInPictureParamsIfNeeded(sourceRecord.getBounds()); + } } mOverrideTaskTransition = options.getOverrideTaskTransition(); @@ -3149,15 +3153,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mWillCloseOrEnterPip = willCloseOrEnterPip; } - /** - * Returns whether this {@link ActivityRecord} is considered closing. Conditions are either - * 1. Is this app animating and was requested to be hidden - * 2. App is delayed closing since it might enter PIP. - */ - boolean isClosingOrEnteringPip() { - return (isAnimating(TRANSITION | PARENTS, ANIMATION_TYPE_APP_TRANSITION) - && !mVisibleRequested) || mWillCloseOrEnterPip; + boolean willCloseOrEnterPip() { + return mWillCloseOrEnterPip; } + /** * @return Whether AppOps allows this package to enter picture-in-picture. */ @@ -3529,7 +3528,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final boolean isCurrentVisible = mVisibleRequested || isState(PAUSED, STARTED); - if (updateVisibility && isCurrentVisible) { + if (updateVisibility && isCurrentVisible + // Avoid intermediate lifecycle change when launching with clearing task. + && !task.isClearingToReuseTask()) { boolean ensureVisibility = false; if (occludesParent(true /* includingFinishing */)) { // If the current activity is not opaque, we need to make sure the visibilities of @@ -5272,12 +5273,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } final int windowsCount = mChildren.size(); + // With Shell-Transition, the activity will running a transition when it is visible. + // It won't be included when fromTransition is true means the call from finishTransition. + final boolean runningAnimation = sEnableShellTransitions ? visible + : isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION); for (int i = 0; i < windowsCount; i++) { - mChildren.get(i).onAppVisibilityChanged(visible, isAnimating(PARENTS, - ANIMATION_TYPE_APP_TRANSITION)); + mChildren.get(i).onAppVisibilityChanged(visible, runningAnimation); } setVisible(visible); setVisibleRequested(visible); + ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "commitVisibility: %s: visible=%b" + + " visibleRequested=%b, isInTransition=%b, runningAnimation=%b, caller=%s", + this, isVisible(), mVisibleRequested, isInTransition(), runningAnimation, + Debug.getCallers(5)); if (!visible) { stopFreezingScreen(true, true); } else { @@ -5300,9 +5308,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A task.dispatchTaskInfoChangedIfNeeded(false /* force */); task = task.getParent().asTask(); } - ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, - "commitVisibility: %s: visible=%b mVisibleRequested=%b", this, - isVisible(), mVisibleRequested); final DisplayContent displayContent = getDisplayContent(); displayContent.getInputMonitor().setUpdateInputWindowsNeededLw(); if (performLayout) { @@ -5338,11 +5343,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_WINDOW_ANIMATION | ANIMATION_TYPE_RECENTS); if (!delayed) { - // We aren't delayed anything, but exiting windows rely on the animation finished - // callback being called in case the ActivityRecord was pretending to be delayed, - // which we might have done because we were in closing/opening apps list. if (!usingShellTransitions) { - onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION, null /* AnimationAdapter */); if (visible) { // The token was made immediately visible, there will be no entrance animation. // We need to inform the client the enter animation was finished. @@ -7737,9 +7738,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // relatively fixed. overrideConfig.colorMode = fullConfig.colorMode; overrideConfig.densityDpi = fullConfig.densityDpi; - // The smallest screen width is the short side of screen bounds. Because the bounds - // and density won't be changed, smallestScreenWidthDp is also fixed. - overrideConfig.smallestScreenWidthDp = fullConfig.smallestScreenWidthDp; if (info.isFixedOrientation()) { // lock rotation too. When in size-compat, onConfigurationChanged will watch for and // apply runtime rotation changes. @@ -7836,7 +7834,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // computed accordingly. if (!matchParentBounds()) { getTaskFragment().computeConfigResourceOverrides(resolvedConfig, - newParentConfiguration); + newParentConfiguration, areBoundsLetterboxed()); } // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds // are already calculated in resolveFixedOrientationConfiguration. @@ -8007,7 +8005,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } // Since bounds has changed, the configuration needs to be computed accordingly. - getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration); + getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, + areBoundsLetterboxed()); } void recomputeConfiguration() { @@ -8223,7 +8222,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Calculate app bounds using fixed orientation bounds because they will be needed later // for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}. getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(), - newParentConfig); + newParentConfig, mCompatDisplayInsets, areBoundsLetterboxed()); mLetterboxBoundsForFixedOrientationAndAspectRatio = new Rect(resolvedBounds); } @@ -8251,7 +8250,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Compute the configuration based on the resolved bounds. If aspect ratio doesn't // restrict, the bounds should be the requested override bounds. getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, - getFixedRotationTransformDisplayInfo()); + getFixedRotationTransformDisplayInfo(), areBoundsLetterboxed()); } } @@ -8315,7 +8314,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // are calculated in compat container space. The actual position on screen will be applied // later, so the calculation is simpler that doesn't need to involve offset from parent. getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration, - mCompatDisplayInsets); + mCompatDisplayInsets, areBoundsLetterboxed()); // Use current screen layout as source because the size of app is independent to parent. resolvedConfig.screenLayout = TaskFragment.computeScreenLayoutOverride( getConfiguration().screenLayout, resolvedConfig.screenWidthDp, @@ -9771,6 +9770,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void setPictureInPictureParams(PictureInPictureParams p) { pictureInPictureArgs.copyOnlySet(p); + adjustPictureInPictureParamsIfNeeded(getBounds()); getTask().getRootTask().onPictureInPictureParamsChanged(); } @@ -9822,6 +9822,18 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A return new Point(windowLayout.minWidth, windowLayout.minHeight); } + /** + * Adjust the source rect hint in {@link #pictureInPictureArgs} by window bounds since + * it is relative to its root view (see also b/235599028). + * It is caller's responsibility to make sure this is called exactly once when we update + * {@link #pictureInPictureArgs} to avoid double offset. + */ + private void adjustPictureInPictureParamsIfNeeded(Rect windowBounds) { + if (pictureInPictureArgs != null && pictureInPictureArgs.hasSourceBoundsHint()) { + pictureInPictureArgs.getSourceRectHint().offset(windowBounds.left, windowBounds.top); + } + } + static class Builder { private final ActivityTaskManagerService mAtmService; private WindowProcessController mCallerApp; diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index a78dbd6bbe60..9be93404dcf1 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -19,7 +19,9 @@ package com.android.server.wm; import static android.app.ActivityManager.START_CANCELED; import static android.app.ActivityManager.START_SUCCESS; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL; @@ -29,6 +31,7 @@ import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.IApplicationThread; import android.content.ComponentName; @@ -46,6 +49,7 @@ import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; import android.view.RemoteAnimationAdapter; +import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; @@ -536,6 +540,42 @@ public class ActivityStartController { .execute(); } + boolean startExistingRecentsIfPossible(Intent intent, ActivityOptions options) { + final int activityType = mService.getRecentTasks().getRecentsComponent() + .equals(intent.getComponent()) ? ACTIVITY_TYPE_RECENTS : ACTIVITY_TYPE_HOME; + final Task rootTask = mService.mRootWindowContainer.getDefaultTaskDisplayArea() + .getRootTask(WINDOWING_MODE_UNDEFINED, activityType); + if (rootTask == null) return false; + final ActivityRecord r = rootTask.topRunningActivity(); + if (r == null || r.mVisibleRequested || !r.attachedToProcess() + || !r.mActivityComponent.equals(intent.getComponent()) + // Recents keeps invisible while device is locked. + || r.mDisplayContent.isKeyguardLocked()) { + return false; + } + mService.mRootWindowContainer.startPowerModeLaunchIfNeeded(true /* forceSend */, r); + final ActivityMetricsLogger.LaunchingState launchingState = + mSupervisor.getActivityMetricsLogger().notifyActivityLaunching(intent); + final Task task = r.getTask(); + mService.deferWindowLayout(); + try { + task.mTransitionController.requestTransitionIfNeeded(WindowManager.TRANSIT_TO_FRONT, + 0 /* flags */, task, task /* readyGroupRef */, + options.getRemoteTransition(), null /* displayChange */); + r.mTransitionController.setTransientLaunch(r, + TaskDisplayArea.getRootTaskAbove(rootTask)); + task.moveToFront("startExistingRecents"); + task.mInResumeTopActivity = true; + task.resumeTopActivity(null /* prev */, options, true /* deferPause */); + mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(launchingState, + ActivityManager.START_TASK_TO_FRONT, false, r, options); + } finally { + task.mInResumeTopActivity = false; + mService.continueWindowLayout(); + } + return true; + } + void registerRemoteAnimationForNextActivityStart(String packageName, RemoteAnimationAdapter adapter, @Nullable IBinder launchCookie) { mPendingRemoteAnimationRegistry.addPendingAnimation(packageName, adapter, launchCookie); diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index d5362a070d8e..890b91025151 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2565,7 +2565,6 @@ class ActivityStarter { mInTask = null; } mInTaskFragment = inTaskFragment; - sendNewTaskFragmentResultRequestIfNeeded(); mStartFlags = startFlags; // If the onlyIfNeeded flag is set, then we can do this if the activity being launched @@ -2608,18 +2607,6 @@ class ActivityStarter { } } - private void sendNewTaskFragmentResultRequestIfNeeded() { - if (mStartActivity.resultTo != null && mInTaskFragment != null - && mInTaskFragment != mStartActivity.resultTo.getTaskFragment()) { - Slog.w(TAG, - "Activity is launching as a new TaskFragment, so cancelling activity result."); - mStartActivity.resultTo.sendResult(INVALID_UID, mStartActivity.resultWho, - mStartActivity.requestCode, RESULT_CANCELED, - null /* data */, null /* dataGrants */); - mStartActivity.resultTo = null; - } - } - private void computeLaunchingTaskFlags() { // If the caller is not coming from another activity, but has given us an explicit task into // which they would like us to launch the new activity, then let's see about doing that. diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index eec77898663c..9d8e0877584f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1231,6 +1231,28 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) { + + final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions); + // A quick path (skip general intent/task resolving) to start recents animation if the + // recents (or home) activity is available in background. + if (opts != null && opts.getOriginalOptions().getTransientLaunch() + && isCallerRecents(Binder.getCallingUid())) { + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "startExistingRecents"); + if (mActivityStartController.startExistingRecentsIfPossible( + intent, opts.getOriginalOptions())) { + return ActivityManager.START_TASK_TO_FRONT; + } + // Else follow the standard launch procedure. + } + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + Binder.restoreCallingIdentity(origId); + } + } + assertPackageMatchesCallingUid(callingPackage); enforceNotIsolatedCaller("startActivityAsUser"); if (Process.isSdkSandboxUid(Binder.getCallingUid())) { @@ -1257,7 +1279,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { .setRequestCode(requestCode) .setStartFlags(startFlags) .setProfilerInfo(profilerInfo) - .setActivityOptions(bOptions) + .setActivityOptions(opts) .setUserId(userId) .execute(); @@ -3521,10 +3543,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mRootWindowContainer.moveActivityToPinnedRootTask(r, null /* launchIntoPipHostActivity */, "enterPictureInPictureMode", transition); - final Task task = r.getTask(); // Continue the pausing process after entering pip. - if (task.getPausingActivity() == r) { - task.schedulePauseActivity(r, false /* userLeaving */, + if (r.isState(PAUSING)) { + r.getTask().schedulePauseActivity(r, false /* userLeaving */, false /* pauseImmediately */, "auto-pip"); } } diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index 2e1d3b1643ac..0af046281cc5 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -120,7 +120,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume } else { mTransitionOp = OP_CHANGE; } - } else if (transitionType != WindowManager.TRANSIT_NONE) { + } else if (displayContent.mTransitionController.isShellTransitionsEnabled()) { mTransitionOp = OP_APP_SWITCH; } else { mTransitionOp = OP_LEGACY; diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index d07cc68af890..9295c18fe80e 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -112,6 +112,7 @@ class BackNavigationController { RemoteAnimationTarget topAppTarget = null; int prevTaskId; int prevUserId; + boolean prepareAnimation; BackNavigationInfo.Builder infoBuilder = new BackNavigationInfo.Builder(); synchronized (wmService.mGlobalLock) { @@ -257,7 +258,8 @@ class BackNavigationController { BackNavigationInfo.typeToString(backType)); // For now, we only animate when going home. - boolean prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME + prepareAnimation = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME + && requestAnimation // Only create a new leash if no leash has been created. // Otherwise return null for animation target to avoid conflict. && !removedWindowContainer.hasCommittedReparentToAnimationLeash(); @@ -292,7 +294,7 @@ class BackNavigationController { } // Special handling for back to home animation - if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && requestAnimation + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation && prevTask != null) { currentTask.mBackGestureStarted = true; // Make launcher show from behind by marking its top activity as visible and @@ -347,7 +349,7 @@ class BackNavigationController { Task finalTask = currentTask; RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( result, finalRemovedWindowContainer, finalBackType, finalTask, - finalprevActivity, requestAnimation)); + finalprevActivity, prepareAnimation)); infoBuilder.setOnBackNavigationDone(onBackNavigationDone); } @@ -381,14 +383,14 @@ class BackNavigationController { private void onBackNavigationDone( Bundle result, WindowContainer<?> windowContainer, int backType, - Task task, ActivityRecord prevActivity, boolean requestAnimation) { + Task task, ActivityRecord prevActivity, boolean prepareAnimation) { SurfaceControl surfaceControl = windowContainer.getSurfaceControl(); boolean triggerBack = result != null && result.getBoolean( BackNavigationInfo.KEY_TRIGGER_BACK); ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " + "task=%s, prevActivity=%s", backType, task, prevActivity); - if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && requestAnimation) { + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && prepareAnimation) { if (triggerBack) { if (surfaceControl != null && surfaceControl.isValid()) { // When going back to home, hide the task surface before it is re-parented to diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 288777bdb324..d9c509c53bb9 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -161,6 +161,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerInternal; +import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.ScreenOrientation; @@ -1362,7 +1363,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void setInsetProvider(@InternalInsetsType int type, WindowContainer win, @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider) { - setInsetProvider(type, win, frameProvider, null /* imeFrameProvider */); + setInsetProvider(type, win, frameProvider, null /* overrideFrameProviders */); } /** @@ -1371,15 +1372,18 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp * @param type The type of inset this window provides. * @param win The window. * @param frameProvider Function to compute the frame, or {@code null} if the just the frame of - * the window should be taken. - * @param imeFrameProvider Function to compute the frame when dispatching insets to the IME, or - * {@code null} if the normal frame should be taken. + * the window should be taken. Only for non-WindowState providers, nav bar + * and status bar. + * @param overrideFrameProviders Functions to compute the frame when dispatching insets to the + * given window types, or {@code null} if the normal frame should + * be taken. */ void setInsetProvider(@InternalInsetsType int type, WindowContainer win, @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider, - @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider) { + @Nullable SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> + overrideFrameProviders) { mInsetsStateController.getSourceProvider(type).setWindowContainer(win, frameProvider, - imeFrameProvider); + overrideFrameProviders); } InsetsStateController getInsetsStateController() { @@ -1684,7 +1688,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return false; } } - if (r.isState(RESUMED) && !r.getRootTask().mInResumeTopActivity) { + if (r.isState(RESUMED) && !r.getTask().mInResumeTopActivity) { // If the activity is executing or has done the lifecycle callback, use normal // rotation animation so the display info can be updated immediately (see // updateDisplayAndOrientation). This prevents a compatibility issue such as @@ -3865,7 +3869,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mAtmService.onImeWindowSetOnDisplayArea(imePid, mImeWindowsContainer); } mInsetsStateController.getSourceProvider(ITYPE_IME).setWindowContainer(win, - mDisplayPolicy.getImeSourceFrameProvider(), null /* imeFrameProvider */); + mDisplayPolicy.getImeSourceFrameProvider(), null); computeImeTarget(true /* updateImeTarget */); updateImeControlTarget(); } @@ -5686,7 +5690,16 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp void getKeepClearAreas(Set<Rect> outRestricted, Set<Rect> outUnrestricted) { final Matrix tmpMatrix = new Matrix(); final float[] tmpFloat9 = new float[9]; + final RecentsAnimationController recentsAnimationController = + mWmService.getRecentsAnimationController(); forAllWindows(w -> { + // Skip the window if it is part of Recents animation + final boolean ignoreRecentsAnimationTarget = recentsAnimationController != null + && recentsAnimationController.shouldApplyInputConsumer(w.getActivityRecord()); + if (ignoreRecentsAnimationTarget) { + return false; // continue traversal + } + if (w.isVisible() && !w.inPinnedWindowingMode()) { w.getKeepClearAreas(outRestricted, outUnrestricted, tmpMatrix, tmpFloat9); } @@ -6508,12 +6521,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp /** * Notifies the remote insets controller that the top focused window has changed. * - * @param packageName The name of the package that is open in the top focused window. + * @param component The application component that is open in the top focussed window. * @param requestedVisibilities The insets visibilities requested by the focussed window. */ - void topFocusedWindowChanged(String packageName, InsetsVisibilities requestedVisibilities) { + void topFocusedWindowChanged(ComponentName component, + InsetsVisibilities requestedVisibilities) { try { - mRemoteInsetsController.topFocusedWindowChanged(packageName, requestedVisibilities); + mRemoteInsetsController.topFocusedWindowChanged(component, requestedVisibilities); } catch (RemoteException e) { Slog.w(TAG, "Failed to deliver package in top focused window change", e); } diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 2b359cadb377..98a51a97110d 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -48,6 +48,7 @@ import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BA import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_INTERCEPT_GLOBAL_DRAG_AND_DROP; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; @@ -1155,7 +1156,7 @@ public class DisplayPolicy { break; case TYPE_NAVIGATION_BAR: mNavigationBar = win; - mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win, + final TriConsumer<DisplayFrames, WindowContainer, Rect> navFrameProvider = (displayFrames, windowContainer, inOutFrame) -> { if (!mNavButtonForcedVisible) { final LayoutParams lp = @@ -1165,18 +1166,22 @@ public class DisplayPolicy { if (provider.type != ITYPE_NAVIGATION_BAR) { continue; } - calculateInsetsFrame(displayFrames, win, inOutFrame, - provider.source, provider.insetsSize - ); + InsetsFrameProvider.calculateInsetsFrame( + displayFrames.mUnrestricted, + win.getBounds(), displayFrames.mDisplayCutoutSafe, + inOutFrame, provider.source, + provider.insetsSize, lp.privateFlags); } } inOutFrame.inset(win.mGivenContentInsets); } - }, - - (displayFrames, windowContainer, inOutFrame) -> { - // For IME, we don't modify the frame. - }); + }; + final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> imeOverride = + new SparseArray<>(); + // For IME, we don't modify the frame. + imeOverride.put(TYPE_INPUT_METHOD, null); + mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win, + navFrameProvider, imeOverride); mDisplayContent.setInsetProvider(ITYPE_BOTTOM_MANDATORY_GESTURES, win, (displayFrames, windowContainer, inOutFrame) -> { @@ -1241,24 +1246,51 @@ public class DisplayPolicy { provider.insetsSize != null ? (displayFrames, windowContainer, inOutFrame) -> { inOutFrame.inset(win.mGivenContentInsets); + final LayoutParams lp = + win.mAttrs.forRotation(displayFrames.mRotation); final InsetsFrameProvider ifp = win.mAttrs.forRotation(displayFrames.mRotation) .providedInsets[index]; - calculateInsetsFrame(displayFrames, windowContainer, - inOutFrame, ifp.source, ifp.insetsSize); - } : null; - final TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider = - provider.imeInsetsSize != null - ? (displayFrames, windowContainer, inOutFrame) -> { - inOutFrame.inset(win.mGivenContentInsets); - final InsetsFrameProvider ifp = - win.mAttrs.forRotation(displayFrames.mRotation) - .providedInsets[index]; - calculateInsetsFrame(displayFrames, windowContainer, - inOutFrame, ifp.source, ifp.imeInsetsSize); + InsetsFrameProvider.calculateInsetsFrame( + displayFrames.mUnrestricted, + windowContainer.getBounds(), + displayFrames.mDisplayCutoutSafe, + inOutFrame, ifp.source, + ifp.insetsSize, lp.privateFlags); } : null; + final InsetsFrameProvider.InsetsSizeOverride[] overrides = + provider.insetsSizeOverrides; + final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> + overrideProviders; + if (overrides != null) { + overrideProviders = new SparseArray<>(); + for (int j = overrides.length - 1; j >= 0; j--) { + final int overrideIndex = j; + final TriConsumer<DisplayFrames, WindowContainer, Rect> + overrideFrameProvider = + (displayFrames, windowContainer, inOutFrame) -> { + final LayoutParams lp = + win.mAttrs.forRotation( + displayFrames.mRotation); + final InsetsFrameProvider ifp = + win.mAttrs.providedInsets[index]; + InsetsFrameProvider.calculateInsetsFrame( + displayFrames.mUnrestricted, + windowContainer.getBounds(), + displayFrames.mDisplayCutoutSafe, + inOutFrame, ifp.source, + ifp.insetsSizeOverrides[ + overrideIndex].insetsSize, + lp.privateFlags); + }; + overrideProviders.put(overrides[j].windowType, + overrideFrameProvider); + } + } else { + overrideProviders = null; + } mDisplayContent.setInsetProvider(provider.type, win, frameProvider, - imeFrameProvider); + overrideProviders); mInsetsSourceWindowsExceptIme.add(win); } } @@ -1266,31 +1298,6 @@ public class DisplayPolicy { } } - private void calculateInsetsFrame(DisplayFrames df, WindowContainer container, Rect inOutFrame, - int source, Insets insetsSize) { - if (source == InsetsFrameProvider.SOURCE_DISPLAY) { - inOutFrame.set(df.mUnrestricted); - } else if (source == InsetsFrameProvider.SOURCE_CONTAINER_BOUNDS) { - inOutFrame.set(container.getBounds()); - } - if (insetsSize == null) { - return; - } - // Only one side of the provider shall be applied. Check in the order of left - top - - // right - bottom, only the first non-zero value will be applied. - if (insetsSize.left != 0) { - inOutFrame.right = inOutFrame.left + insetsSize.left; - } else if (insetsSize.top != 0) { - inOutFrame.bottom = inOutFrame.top + insetsSize.top; - } else if (insetsSize.right != 0) { - inOutFrame.left = inOutFrame.right - insetsSize.right; - } else if (insetsSize.bottom != 0) { - inOutFrame.top = inOutFrame.bottom - insetsSize.bottom; - } else { - inOutFrame.setEmpty(); - } - } - @WindowManagerPolicy.AltBarPosition private int getAltBarPosition(WindowManager.LayoutParams params) { switch (params.gravity) { diff --git a/services/core/java/com/android/server/wm/EventLogTags.logtags b/services/core/java/com/android/server/wm/EventLogTags.logtags index 6d63331622b9..1e5a219e5e52 100644 --- a/services/core/java/com/android/server/wm/EventLogTags.logtags +++ b/services/core/java/com/android/server/wm/EventLogTags.logtags @@ -66,4 +66,4 @@ option java_package com.android.server.wm 31007 wm_boot_animation_done (time|2|3) # Request surface flinger to show / hide the wallpaper surface. -33001 wm_wallpaper_surface (Display Id|1|5),(visible|1) +33001 wm_wallpaper_surface (Display Id|1|5),(Visible|1),(Target|3) diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 0d4cfa3a8128..3e6e06a27fdc 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -234,7 +234,7 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider // private static boolean isImeLayeringTarget(@NonNull InsetsControlTarget target, @NonNull InsetsControlTarget dcTarget) { - return !dcTarget.getWindow().isClosing() && target == dcTarget; + return !isImeTargetWindowClosing(dcTarget.getWindow()) && target == dcTarget; } private static boolean isAboveImeLayeringTarget(@NonNull InsetsControlTarget target, @@ -256,7 +256,14 @@ final class ImeInsetsSourceProvider extends WindowContainerInsetsSourceProvider final InsetsControlTarget target = mDisplayContent.getImeTarget(IME_TARGET_CONTROL); return target == mImeRequester && (mImeRequester.getWindow() == null - || !mImeRequester.getWindow().isClosing()); + || !isImeTargetWindowClosing(mImeRequester.getWindow())); + } + + private static boolean isImeTargetWindowClosing(@NonNull WindowState win) { + return win.mAnimatingExit || win.mActivityRecord != null + && (win.mActivityRecord.isInTransition() + && !win.mActivityRecord.isVisibleRequested() + || win.mActivityRecord.willCloseOrEnterPip()); } private boolean isTargetChangedWithinActivity(InsetsControlTarget target) { diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index b7ddbd070460..33cdd2e98113 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -265,7 +265,7 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal .setContainerLayer() .setName(name) .setCallsite("createSurfaceForGestureMonitor") - .setParent(dc.getOverlayLayer()) + .setParent(dc.getSurfaceControl()) .build(); } } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 8389dbde3e14..2ce333d5438b 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -64,7 +64,10 @@ import android.view.SurfaceControl; import android.view.WindowManager; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.protolog.common.ProtoLog; +import com.android.server.LocalServices; +import com.android.server.inputmethod.InputMethodManagerInternal; import java.io.PrintWriter; import java.lang.ref.WeakReference; @@ -405,8 +408,28 @@ final class InputMonitor { // Shell transitions doesn't use RecentsAnimationController || getWeak(mActiveRecentsActivity) != null && focus.inTransition(); if (shouldApplyRecentsInputConsumer) { - requestFocus(recentsAnimationInputConsumer.mWindowHandle.token, - recentsAnimationInputConsumer.mName); + if (mInputFocus != recentsAnimationInputConsumer.mWindowHandle.token) { + requestFocus(recentsAnimationInputConsumer.mWindowHandle.token, + recentsAnimationInputConsumer.mName); + // Hiding IME/IME icon when recents input consumer gain focus. + if (!mDisplayContent.isImeAttachedToApp()) { + // Hiding IME if IME window is not attached to app since it's not proper to + // snapshot Task with IME window to animate together in this case. + final InputMethodManagerInternal inputMethodManagerInternal = + LocalServices.getService(InputMethodManagerInternal.class); + if (inputMethodManagerInternal != null) { + inputMethodManagerInternal.hideCurrentInputMethod( + SoftInputShowHideReason.HIDE_RECENTS_ANIMATION); + } + } else { + // Disable IME icon explicitly when IME attached to the app in case + // IME icon might flickering while swiping to the next app task still + // in animating before the next app window focused, or IME icon + // persists on the bottom when swiping the task to recents. + InputMethodManagerInternal.get().updateImeWindowStatus( + true /* disableImeIcon */); + } + } return; } } diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index db2ee2de06ca..3e2d7c928936 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -46,6 +46,7 @@ import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.StatusBarManager; import android.app.WindowConfiguration; +import android.content.ComponentName; import android.content.res.Resources; import android.graphics.Rect; import android.util.ArrayMap; @@ -265,7 +266,7 @@ class InsetsPolicy { state = originalState; } state = adjustVisibilityForIme(target, state, state == originalState); - return adjustInsetsForRoundedCorners(target, state, state == originalState); + return adjustInsetsForRoundedCorners(target.mToken, state, state == originalState); } InsetsState adjustInsetsForWindow(WindowState target, InsetsState originalState) { @@ -289,8 +290,9 @@ class InsetsPolicy { // contains all insets types. final InsetsState originalState = mDisplayContent.getInsetsPolicy() .enforceInsetsPolicyForTarget(type, WINDOWING_MODE_FULLSCREEN, alwaysOnTop, - mStateController.getRawInsetsState()); - return adjustVisibilityForTransientTypes(originalState); + attrs.type, mStateController.getRawInsetsState()); + InsetsState state = adjustVisibilityForTransientTypes(originalState); + return adjustInsetsForRoundedCorners(token, state, state == originalState); } /** @@ -350,12 +352,13 @@ class InsetsPolicy { * @param type the inset type provided by the target * @param windowingMode the windowing mode of the target * @param isAlwaysOnTop is the target always on top + * @param windowType the type of the target * @param state the input inset state containing all the sources * @return The state stripped of the necessary information. */ InsetsState enforceInsetsPolicyForTarget(@InternalInsetsType int type, @WindowConfiguration.WindowingMode int windowingMode, boolean isAlwaysOnTop, - InsetsState state) { + int windowType, InsetsState state) { boolean stateCopied = false; if (type != ITYPE_INVALID) { @@ -376,21 +379,20 @@ class InsetsPolicy { if (type == ITYPE_STATUS_BAR || type == ITYPE_CLIMATE_BAR) { state.removeSource(ITYPE_CAPTION_BAR); } - - // IME needs different frames for certain cases (e.g. navigation bar in gesture nav). - if (type == ITYPE_IME) { - ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController - .getSourceProviders(); - for (int i = providers.size() - 1; i >= 0; i--) { - WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i); - if (otherProvider.overridesImeFrame()) { - InsetsSource override = - new InsetsSource( - state.getSource(otherProvider.getSource().getType())); - override.setFrame(otherProvider.getImeOverrideFrame()); - state.addSource(override); - } + } + ArrayMap<Integer, WindowContainerInsetsSourceProvider> providers = mStateController + .getSourceProviders(); + for (int i = providers.size() - 1; i >= 0; i--) { + WindowContainerInsetsSourceProvider otherProvider = providers.valueAt(i); + if (otherProvider.overridesFrame(windowType)) { + if (!stateCopied) { + state = new InsetsState(state); + stateCopied = true; } + InsetsSource override = + new InsetsSource(state.getSource(otherProvider.getSource().getType())); + override.setFrame(otherProvider.getOverriddenFrame(windowType)); + state.addSource(override); } } @@ -465,15 +467,19 @@ class InsetsPolicy { return originalState; } - private InsetsState adjustInsetsForRoundedCorners(WindowState w, InsetsState originalState, + private InsetsState adjustInsetsForRoundedCorners(WindowToken token, InsetsState originalState, boolean copyState) { - final Task task = w.getTask(); - if (task != null && !task.getWindowConfiguration().tasksAreFloating()) { - // Use task bounds to calculating rounded corners if the task is not floating. - final Rect roundedCornerFrame = new Rect(task.getBounds()); - final InsetsState state = copyState ? new InsetsState(originalState) : originalState; - state.setRoundedCornerFrame(roundedCornerFrame); - return state; + if (token != null) { + final ActivityRecord activityRecord = token.asActivityRecord(); + final Task task = activityRecord != null ? activityRecord.getTask() : null; + if (task != null && !task.getWindowConfiguration().tasksAreFloating()) { + // Use task bounds to calculating rounded corners if the task is not floating. + final Rect roundedCornerFrame = new Rect(task.getBounds()); + final InsetsState state = copyState ? new InsetsState(originalState) + : originalState; + state.setRoundedCornerFrame(roundedCornerFrame); + return state; + } } return originalState; } @@ -543,8 +549,10 @@ class InsetsPolicy { return focusedWin; } if (remoteInsetsControllerControlsSystemBars(focusedWin)) { + ComponentName component = focusedWin.mActivityRecord != null + ? focusedWin.mActivityRecord.mActivityComponent : null; mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged( - focusedWin.mAttrs.packageName, focusedWin.getRequestedVisibilities()); + component, focusedWin.getRequestedVisibilities()); return mDisplayContent.mRemoteInsetsControlTarget; } if (mPolicy.areSystemBarsForcedShownLw()) { @@ -601,8 +609,10 @@ class InsetsPolicy { return null; } if (remoteInsetsControllerControlsSystemBars(focusedWin)) { + ComponentName component = focusedWin.mActivityRecord != null + ? focusedWin.mActivityRecord.mActivityComponent : null; mDisplayContent.mRemoteInsetsControlTarget.topFocusedWindowChanged( - focusedWin.mAttrs.packageName, focusedWin.getRequestedVisibilities()); + component, focusedWin.getRequestedVisibilities()); return mDisplayContent.mRemoteInsetsControlTarget; } if (mPolicy.areSystemBarsForcedShownLw()) { diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 610bf0594c66..86a73c935e52 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -27,7 +27,6 @@ import static com.android.server.wm.InsetsSourceProviderProto.CONTROL_TARGET; import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL; import static com.android.server.wm.InsetsSourceProviderProto.FAKE_CONTROL_TARGET; import static com.android.server.wm.InsetsSourceProviderProto.FRAME; -import static com.android.server.wm.InsetsSourceProviderProto.IME_OVERRIDDEN_FRAME; import static com.android.server.wm.InsetsSourceProviderProto.IS_LEASH_READY_FOR_DISPATCHING; import static com.android.server.wm.InsetsSourceProviderProto.PENDING_CONTROL_TARGET; import static com.android.server.wm.InsetsSourceProviderProto.SEAMLESS_ROTATING; @@ -41,6 +40,7 @@ import android.annotation.Nullable; import android.graphics.Insets; import android.graphics.Point; import android.graphics.Rect; +import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import android.view.InsetsFrameProvider; import android.view.InsetsSource; @@ -78,8 +78,8 @@ abstract class InsetsSourceProvider { private @Nullable ControlAdapter mAdapter; private TriConsumer<DisplayFrames, WindowContainer, Rect> mFrameProvider; - private TriConsumer<DisplayFrames, WindowContainer, Rect> mImeFrameProvider; - private final Rect mImeOverrideFrame = new Rect(); + private SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> mOverrideFrameProviders; + private final SparseArray<Rect> mOverrideFrames = new SparseArray<Rect>(); private boolean mIsLeashReadyForDispatching; private final Rect mSourceFrame = new Rect(); private final Rect mLastSourceFrame = new Rect(); @@ -146,12 +146,15 @@ abstract class InsetsSourceProvider { * @param windowContainer The window container that links to this source. * @param frameProvider Based on display frame state and the window, calculates the resulting * frame that should be reported to clients. - * @param imeFrameProvider Based on display frame state and the window, calculates the resulting - * frame that should be reported to IME. + * This will only be used when the window container providing the insets is + * not a WindowState. + * @param overrideFrameProviders Based on display frame state and the window, calculates the + * resulting frame that should be reported to given window type. */ void setWindowContainer(@Nullable WindowContainer windowContainer, @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> frameProvider, - @Nullable TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider) { + @Nullable SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> + overrideFrameProviders) { if (mWindowContainer != null) { if (mControllable) { mWindowContainer.setControllableInsetProvider(null); @@ -167,8 +170,9 @@ abstract class InsetsSourceProvider { ProtoLog.d(WM_DEBUG_WINDOW_INSETS, "InsetsSource setWin %s for type %s", windowContainer, InsetsState.typeToString(mSource.getType())); mWindowContainer = windowContainer; + // TODO: remove the frame provider for non-WindowState container. mFrameProvider = frameProvider; - mImeFrameProvider = imeFrameProvider; + mOverrideFrameProviders = overrideFrameProviders; if (windowContainer == null) { setServerVisible(false); mSource.setVisibleFrame(null); @@ -228,10 +232,25 @@ abstract class InsetsSourceProvider { } updateSourceFrameForServerVisibility(); - if (mImeFrameProvider != null) { - mImeOverrideFrame.set(frame); - mImeFrameProvider.accept(mWindowContainer.getDisplayContent().mDisplayFrames, - mWindowContainer, mImeOverrideFrame); + if (mOverrideFrameProviders != null) { + for (int i = mOverrideFrameProviders.size() - 1; i >= 0; i--) { + final int windowType = mOverrideFrameProviders.keyAt(i); + final Rect overrideFrame; + if (mOverrideFrames.contains(windowType)) { + overrideFrame = mOverrideFrames.get(windowType); + overrideFrame.set(frame); + } else { + overrideFrame = new Rect(frame); + } + final TriConsumer<DisplayFrames, WindowContainer, Rect> provider = + mOverrideFrameProviders.get(windowType); + if (provider != null) { + mOverrideFrameProviders.get(windowType).accept( + mWindowContainer.getDisplayContent().mDisplayFrames, mWindowContainer, + overrideFrame); + } + mOverrideFrames.put(windowType, overrideFrame); + } } if (win.mGivenVisibleInsets.left != 0 || win.mGivenVisibleInsets.top != 0 @@ -552,32 +571,30 @@ abstract class InsetsSourceProvider { return mClientVisible; } - /** - * @return Whether this provider uses a different frame to dispatch to the IME. - */ - boolean overridesImeFrame() { - return mImeFrameProvider != null; + boolean overridesFrame(int windowType) { + return mOverrideFrames.contains(windowType); } - /** - * @return Rect to dispatch to the IME as frame. Only valid if {@link #overridesImeFrame()} - * returns {@code true}. - */ - Rect getImeOverrideFrame() { - return mImeOverrideFrame; + Rect getOverriddenFrame(int windowType) { + return mOverrideFrames.get(windowType); } public void dump(PrintWriter pw, String prefix) { pw.println(prefix + getClass().getSimpleName()); prefix = prefix + " "; pw.print(prefix + "mSource="); mSource.dump("", pw); + pw.print(prefix + "mSourceFrame="); + pw.println(mSourceFrame); + if (mOverrideFrames.size() > 0) { + pw.print(prefix + "mOverrideFrames="); + pw.println(mOverrideFrames); + } if (mControl != null) { pw.print(prefix + "mControl="); mControl.dump("", pw); } pw.print(prefix); pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching); - pw.print(" mImeOverrideFrame="); pw.print(mImeOverrideFrame.toShortString()); pw.println(); if (mWindowContainer != null) { pw.print(prefix + "mWindowContainer="); @@ -621,7 +638,6 @@ abstract class InsetsSourceProvider { if (mAdapter != null && mAdapter.mCapturedLeash != null) { mAdapter.mCapturedLeash.dumpDebug(proto, CAPTURED_LEASH); } - mImeOverrideFrame.dumpDebug(proto, IME_OVERRIDDEN_FRAME); proto.write(IS_LEASH_READY_FOR_DISPATCHING, mIsLeashReadyForDispatching); proto.write(CLIENT_VISIBLE, mClientVisible); proto.write(SERVER_VISIBLE, mServerVisible); diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index ca4376e0f263..d76f6be93aeb 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -170,11 +170,12 @@ class KeyguardController { final KeyguardDisplayState state = getDisplayState(displayId); final boolean aodChanged = aodShowing != state.mAodShowing; + final boolean aodRemoved = state.mAodShowing && !aodShowing; // If keyguard is going away, but SystemUI aborted the transition, need to reset state. - // Do not reset keyguardChanged status if this is aodChanged. + // Do not reset keyguardChanged status when only AOD is removed. final boolean keyguardChanged = (keyguardShowing != state.mKeyguardShowing) - || (state.mKeyguardGoingAway && keyguardShowing && !aodChanged); - if (aodChanged && !aodShowing) { + || (state.mKeyguardGoingAway && keyguardShowing && !aodRemoved); + if (aodRemoved) { updateDeferWakeTransition(false /* waiting */); } if (!keyguardChanged && !aodChanged) { diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index d2ce048ca88f..53f1fe6abec5 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -70,10 +70,7 @@ import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.inputmethod.SoftInputShowHideReason; -import com.android.internal.os.BackgroundThread; import com.android.internal.protolog.common.ProtoLog; -import com.android.internal.util.LatencyTracker; import com.android.internal.util.function.pooled.PooledConsumer; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; @@ -99,11 +96,6 @@ import java.util.stream.Collectors; public class RecentsAnimationController implements DeathRecipient { private static final String TAG = RecentsAnimationController.class.getSimpleName(); private static final long FAILSAFE_DELAY = 1000; - /** - * If the recents animation is canceled before the delay since the window drawn, do not log the - * action because the duration is too small that may be just a mistouch. - */ - private static final long LATENCY_TRACKER_LOG_DELAY_MS = 300; // Constant for a yet-to-be-calculated {@link RemoteAnimationTarget#Mode} state private static final int MODE_UNKNOWN = -1; @@ -144,7 +136,7 @@ public class RecentsAnimationController implements DeathRecipient { private boolean mPendingStart = true; // Set when the animation has been canceled - private volatile boolean mCanceled; + private boolean mCanceled; // Whether or not the input consumer is enabled. The input consumer must be both registered and // enabled for it to start intercepting touch events. @@ -333,26 +325,6 @@ public class RecentsAnimationController implements DeathRecipient { } } InputMethodManagerInternal.get().maybeFinishStylusHandwriting(); - if (!behindSystemBars) { - // Hiding IME if IME window is not attached to app. - // Since some windowing mode is not proper to snapshot Task with IME window - // while the app transitioning to the next task (e.g. split-screen mode) - if (!mDisplayContent.isImeAttachedToApp()) { - final InputMethodManagerInternal inputMethodManagerInternal = - LocalServices.getService(InputMethodManagerInternal.class); - if (inputMethodManagerInternal != null) { - inputMethodManagerInternal.hideCurrentInputMethod( - SoftInputShowHideReason.HIDE_RECENTS_ANIMATION); - } - } else { - // Disable IME icon explicitly when IME attached to the app in case - // IME icon might flickering while swiping to the next app task still - // in animating before the next app window focused, or IME icon - // persists on the bottom when swiping the task to recents. - InputMethodManagerInternal.get().updateImeWindowStatus( - true /* disableImeIcon */); - } - } mService.mWindowPlacerLocked.requestTraversal(); } } finally { @@ -380,10 +352,6 @@ public class RecentsAnimationController implements DeathRecipient { } } - // TODO(b/166736352): Remove this method without the need to expose to launcher. - @Override - public void hideCurrentInputMethod() { } - @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { synchronized (mService.mGlobalLock) { @@ -785,15 +753,6 @@ public class RecentsAnimationController implements DeathRecipient { }, false /* traverseTopToBottom */); } - void logRecentsAnimationStartTime(int durationMs) { - BackgroundThread.getHandler().postDelayed(() -> { - if (!mCanceled) { - mService.mLatencyTracker.logAction(LatencyTracker.ACTION_START_RECENTS_ANIMATION, - durationMs); - } - }, LATENCY_TRACKER_LOG_DELAY_MS); - } - private boolean removeTaskInternal(int taskId) { boolean result = false; for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index db730e0cb368..dfce40b874c0 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -2118,7 +2118,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // entering content-pip animation. mWindowManager.mTaskSnapshotController.recordTaskSnapshot( task, false /* allowSnapshotHome */); - rootTask.setBounds(r.getOptions().getLaunchBounds()); + rootTask.setBounds(r.pictureInPictureArgs.getSourceRectHint()); } rootTask.setDeferTaskAppear(false); diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java index baa31a073dd2..2879e33fb71a 100644 --- a/services/core/java/com/android/server/wm/SafeActivityOptions.java +++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java @@ -18,6 +18,7 @@ package com.android.server.wm; import static android.Manifest.permission.CONTROL_KEYGUARD; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; +import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.STATUS_BAR_SERVICE; import static android.app.ActivityTaskManager.INVALID_TASK_ID; @@ -247,6 +248,14 @@ public class SafeActivityOptions { throw new SecurityException(msg); } } + if (options.getTransientLaunch() && !supervisor.mRecentTasks.isCallerRecents(callingUid) + && ActivityTaskManagerService.checkPermission( + MANAGE_ACTIVITY_TASKS, callingPid, callingUid) == PERMISSION_DENIED) { + final String msg = "Permission Denial: starting transient launch from " + callerApp + + ", pid=" + callingPid + ", uid=" + callingUid; + Slog.w(TAG, msg); + throw new SecurityException(msg); + } // Check if the caller is allowed to launch on the specified display area. final WindowContainerToken daToken = options.getLaunchTaskDisplayArea(); final TaskDisplayArea taskDisplayArea = daToken != null diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index f80e732c8212..8cad16509c4c 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -173,11 +173,6 @@ class ScreenRotationAnimation { if (isSizeChanged) { mRoundedCornerOverlay = displayContent.findRoundedCornerOverlays(); - } else { - // Exclude rounded corner overlay from screenshot buffer. Rounded - // corner overlay windows are un-rotated during rotation animation - // for a seamless transition. - builder.setExcludeLayers(displayContent.findRoundedCornerOverlays()); } SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = @@ -600,7 +595,7 @@ class ScreenRotationAnimation { } private SurfaceAnimator startDisplayRotation() { - return startAnimation(initializeBuilder() + SurfaceAnimator animator = startAnimation(initializeBuilder() .setAnimationLeashParent(mDisplayContent.getSurfaceControl()) .setSurfaceControl(mDisplayContent.getWindowingLayer()) .setParentSurfaceControl(mDisplayContent.getSurfaceControl()) @@ -609,6 +604,13 @@ class ScreenRotationAnimation { .build(), createWindowAnimationSpec(mRotateEnterAnimation), this::onAnimationEnd); + + // Crop the animation leash to avoid extended wallpaper from showing over + // mBackColorSurface + Rect displayBounds = mDisplayContent.getBounds(); + mDisplayContent.getPendingTransaction() + .setWindowCrop(animator.mLeash, displayBounds.width(), displayBounds.height()); + return animator; } private SurfaceAnimator startScreenshotAlphaAnimation() { diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index f8a9d4665acc..21c5886f085b 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -1957,29 +1957,37 @@ class TaskFragment extends WindowContainer<WindowContainer> { void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig) { computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, - null /* compatInsets */); + null /* compatInsets */, false /* areBoundsLetterboxed */); } void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, - @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo) { + @NonNull Configuration parentConfig, boolean areBoundsLetterboxed) { + computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, + null /* compatInsets */, areBoundsLetterboxed); + } + + void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, + @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo, + boolean areBoundsLetterboxed) { if (overrideDisplayInfo != null) { // Make sure the screen related configs can be computed by the provided display info. inOutConfig.screenLayout = Configuration.SCREENLAYOUT_UNDEFINED; invalidateAppBoundsConfig(inOutConfig); } computeConfigResourceOverrides(inOutConfig, parentConfig, overrideDisplayInfo, - null /* compatInsets */); + null /* compatInsets */, areBoundsLetterboxed); } void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig, - @Nullable ActivityRecord.CompatDisplayInsets compatInsets) { + @Nullable ActivityRecord.CompatDisplayInsets compatInsets, + boolean areBoundsLetterboxed) { if (compatInsets != null) { // Make sure the app bounds can be computed by the compat insets. invalidateAppBoundsConfig(inOutConfig); } computeConfigResourceOverrides(inOutConfig, parentConfig, null /* overrideDisplayInfo */, - compatInsets); + compatInsets, areBoundsLetterboxed); } /** @@ -2006,7 +2014,8 @@ class TaskFragment extends WindowContainer<WindowContainer> { **/ void computeConfigResourceOverrides(@NonNull Configuration inOutConfig, @NonNull Configuration parentConfig, @Nullable DisplayInfo overrideDisplayInfo, - @Nullable ActivityRecord.CompatDisplayInsets compatInsets) { + @Nullable ActivityRecord.CompatDisplayInsets compatInsets, + boolean areBoundsLetterboxed) { int windowingMode = inOutConfig.windowConfiguration.getWindowingMode(); if (windowingMode == WINDOWING_MODE_UNDEFINED) { windowingMode = parentConfig.windowConfiguration.getWindowingMode(); @@ -2113,6 +2122,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { : overrideScreenHeightDp; } + // TODO(b/238331848): Consider simplifying logic that computes smallestScreenWidthDp. if (inOutConfig.smallestScreenWidthDp == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { // When entering to or exiting from Pip, the PipTaskOrganizer will set the @@ -2128,9 +2138,10 @@ class TaskFragment extends WindowContainer<WindowContainer> { // task, because they should not be affected by insets. 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. + } else if (isEmbedded() || areBoundsLetterboxed || customContainerPolicy) { + // For embedded TFs and activities that are letteboxed or eligible for size + // compat mode, the smallest width should be updated. Otherwise, inherit from + // the parent task would result in applications loaded wrong resource. inOutConfig.smallestScreenWidthDp = Math.min(inOutConfig.screenWidthDp, inOutConfig.screenHeightDp); } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index c5993e1ef4bc..801665862d29 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -527,11 +527,12 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mService.mRootWindowContainer.forAllTasks((task) -> { boolean returnTask = !task.mCreatedByOrganizer; task.updateTaskOrganizerState(returnTask /* skipTaskAppeared */); - if (returnTask) { - SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task, + // It is possible for the task to not yet have a surface control, so ensure that + // the update succeeded in setting the organizer for the task before returning + if (task.isOrganized() && returnTask) { + SurfaceControl taskLeash = state.addTaskWithoutCallback(task, "TaskOrganizerController.registerTaskOrganizer"); - taskInfos.add( - new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl)); + taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), taskLeash)); } }); }; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 09f6110b517f..91f69a55d400 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -46,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_EMBEDDED; 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; @@ -84,11 +85,9 @@ import android.window.TransitionInfo; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; -import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.protolog.ProtoLogGroup; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.util.function.pooled.PooledLambda; -import com.android.server.LocalServices; import com.android.server.inputmethod.InputMethodManagerInternal; import java.lang.annotation.Retention; @@ -533,9 +532,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } // Need to update layers on involved displays since they were all paused while // the animation played. This puts the layers back into the correct order. - for (int i = displays.size() - 1; i >= 0; --i) { - if (displays.valueAt(i) == null) continue; - displays.valueAt(i).assignChildLayers(t); + mController.mBuildingFinishLayers = true; + try { + for (int i = displays.size() - 1; i >= 0; --i) { + if (displays.valueAt(i) == null) continue; + displays.valueAt(i).assignChildLayers(t); + } + } finally { + mController.mBuildingFinishLayers = false; } if (rootLeash.isValid()) { t.reparent(rootLeash, null); @@ -953,26 +957,6 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe } } - // Hiding IME/IME icon when starting quick-step with resents animation. - if (!mTargetDisplays.get(mRecentsDisplayId).isImeAttachedToApp()) { - // Hiding IME if IME window is not attached to app. - // Since some windowing mode is not proper to snapshot Task with IME window - // while the app transitioning to the next task (e.g. split-screen mode) - final InputMethodManagerInternal inputMethodManagerInternal = - LocalServices.getService(InputMethodManagerInternal.class); - if (inputMethodManagerInternal != null) { - inputMethodManagerInternal.hideCurrentInputMethod( - SoftInputShowHideReason.HIDE_RECENTS_ANIMATION); - } - } else { - // Disable IME icon explicitly when IME attached to the app in case - // IME icon might flickering while swiping to the next app task still - // in animating before the next app window focused, or IME icon - // persists on the bottom when swiping the task to recents. - InputMethodManagerInternal.get().updateImeWindowStatus( - true /* disableImeIcon */); - } - // The rest of this function handles nav-bar reparenting if (!dc.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition() @@ -1756,6 +1740,9 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe if (occludesKeyguard(wc)) { flags |= FLAG_OCCLUDES_KEYGUARD; } + if (wc.isEmbedded()) { + flags |= FLAG_IS_EMBEDDED; + } return flags; } } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index dbc2c5fb0ce3..88572a937156 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -100,6 +100,14 @@ class TransitionController { // TODO(b/188595497): remove when not needed. final StatusBarManagerInternal mStatusBar; + /** + * `true` when building surface layer order for the finish transaction. We want to prevent + * wm from touching z-order of surfaces during transitions, but we still need to be able to + * calculate the layers for the finishTransaction. So, when assigning layers into the finish + * transaction, set this to true so that the {@link canAssignLayers} will allow it. + */ + boolean mBuildingFinishLayers = false; + TransitionController(ActivityTaskManagerService atm, TaskSnapshotController taskSnapshotController, TransitionTracer transitionTracer) { @@ -309,6 +317,15 @@ class TransitionController { return false; } + /** + * Whether WM can assign layers to window surfaces at this time. This is usually false while + * playing, but can be "opened-up" for certain transition operations like calculating layers + * for finishTransaction. + */ + boolean canAssignLayers() { + return mBuildingFinishLayers || !isPlaying(); + } + @WindowConfiguration.WindowingMode int getWindowingModeAtStart(@NonNull WindowContainer wc) { if (mCollectingTransition == null) return wc.getWindowingMode(); diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 33c0fe13af11..d9b25adb8c4c 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -429,6 +429,13 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (insetsTypes == null || insetsTypes.length == 0) { throw new IllegalArgumentException("Insets type not specified."); } + if (mDisplayContent == null) { + // This is possible this container is detached when WM shell is responding to a previous + // request. WM shell will be updated when this container is attached again and the + // insets need to be updated. + Slog.w(TAG, "Can't add local rect insets source provider when detached. " + this); + return; + } if (mLocalInsetsSourceProviders == null) { mLocalInsetsSourceProviders = new SparseArray<>(); } @@ -1011,6 +1018,9 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< if (dc != null && dc != this) { dc.getPendingTransaction().merge(mPendingTransaction); } + if (dc != this && mLocalInsetsSourceProviders != null) { + mLocalInsetsSourceProviders.clear(); + } for (int i = mChildren.size() - 1; i >= 0; --i) { final WindowContainer child = mChildren.get(i); child.onDisplayChanged(dc); @@ -2473,7 +2483,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< void assignLayer(Transaction t, int layer) { // Don't assign layers while a transition animation is playing // TODO(b/173528115): establish robust best-practices around z-order fighting. - if (mTransitionController.isPlaying()) return; + if (!mTransitionController.canAssignLayers()) return; final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null; if (mSurfaceControl != null && changed) { setLayer(t, layer); @@ -3216,6 +3226,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< void resetSurfacePositionForAnimationLeash(Transaction t) { t.setPosition(mSurfaceControl, 0, 0); + if (mSyncState != SYNC_STATE_NONE && t != mSyncTransaction) { + // Avoid restoring to old position if the sync transaction is applied later. + mSyncTransaction.setPosition(mSurfaceControl, 0, 0); + } mLastSurfacePosition.set(0, 0); } @@ -3756,6 +3770,15 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * hierarchy change implies a configuration change. */ private void onSyncReparent(WindowContainer oldParent, WindowContainer newParent) { + // Check if this is changing displays. If so, mark the old display as "ready" for + // transitions. This is to work around the problem where setting readiness against this + // container will only set the new display as ready and leave the old display as unready. + if (mSyncState != SYNC_STATE_NONE && oldParent != null + && oldParent.getDisplayContent() != null && (newParent == null + || oldParent.getDisplayContent() != newParent.getDisplayContent())) { + mTransitionController.setReady(oldParent.getDisplayContent()); + } + if (newParent == null || newParent.mSyncState == SYNC_STATE_NONE) { if (mSyncState == SYNC_STATE_NONE) { return; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 06d41c0e8d35..9d6e250e8a47 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1020,7 +1020,7 @@ public class WindowManagerService extends IWindowManager.Stub private int mExitAnimId, mEnterAnimId; /** The display that the rotation animation is applying to. */ - private int mFrozenDisplayId; + private int mFrozenDisplayId = INVALID_DISPLAY; /** Skip repeated ActivityRecords initialization. Note that AppWindowsToken's version of this * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */ @@ -5968,10 +5968,10 @@ public class WindowManagerService extends IWindowManager.Stub } void makeWindowFreezingScreenIfNeededLocked(WindowState w) { - // If the screen is currently frozen or off, then keep - // it frozen/off until this window draws at its new - // orientation. - if (!w.mToken.okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) { + // If the screen is currently frozen, then keep it frozen until this window draws at its + // new orientation. + if (mFrozenDisplayId != INVALID_DISPLAY && mFrozenDisplayId == w.getDisplayId() + && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) { ProtoLog.v(WM_DEBUG_ORIENTATION, "Changing surface while display frozen: %s", w); w.setOrientationChanging(true); if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) { @@ -8262,7 +8262,7 @@ public class WindowManagerService extends IWindowManager.Stub .setContainerLayer() .setName("IME Handwriting Surface") .setCallsite("getHandwritingSurfaceForDisplay") - .setParent(dc.getOverlayLayer()) + .setParent(dc.getSurfaceControl()) .build(); } } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 97dcb7574e3c..6bb5eceec84e 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -817,7 +817,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } if (parent.isAllowedToEmbedActivity(activity) != EMBEDDING_ALLOWED) { final Throwable exception = new SecurityException( - "The task fragment is not trusted to embed the given activity."); + "The task fragment is not allowed to embed the given activity."); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } @@ -827,11 +827,6 @@ 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; @@ -1240,8 +1235,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub + " task=" + task); return false; } - if (!ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType()) - || !ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) { + if (!ArrayUtils.isEmpty(hop.getActivityTypes()) + && !ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())) { + return false; + } + if (!ArrayUtils.isEmpty(hop.getWindowingModes()) + && !ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) { return false; } if (isLockTaskModeViolation(finalNewParent, task, isInLockTaskMode)) { @@ -1673,10 +1672,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub // We are reparenting activities to a new embedded TaskFragment, this operation is only // allowed if the new parent is trusted by all reparent activities. final boolean isEmbeddingDisallowed = oldParent.forAllActivities(activity -> - newParentTF.isAllowedToEmbedActivity(activity) == EMBEDDING_ALLOWED); + newParentTF.isAllowedToEmbedActivity(activity) != EMBEDDING_ALLOWED); if (isEmbeddingDisallowed) { final Throwable exception = new SecurityException( - "The new parent is not trusted to embed the activities."); + "The new parent is not allowed to embed the activities."); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); return; } @@ -1693,14 +1692,6 @@ 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/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 46091d842c2a..af8c4c8e9370 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1703,7 +1703,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mFrozenInsetsState != null ? mFrozenInsetsState : getMergedInsetsState(); final InsetsState insetsStateForWindow = insetsPolicy .enforceInsetsPolicyForTarget(insetTypeProvidedByWindow, - getWindowingMode(), isAlwaysOnTop(), rawInsetsState); + getWindowingMode(), isAlwaysOnTop(), mAttrs.type, rawInsetsState); return insetsPolicy.adjustInsetsForWindow(this, insetsStateForWindow, includeTransient); } @@ -3461,10 +3461,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mClient.asBinder().isBinderAlive(); } - boolean isClosing() { - return mAnimatingExit || (mActivityRecord != null && mActivityRecord.isClosingOrEnteringPip()); - } - void sendAppVisibilityToClients() { super.sendAppVisibilityToClients(); diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index fd379bf1d9f4..4a5c4737cbbc 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -126,8 +126,10 @@ class WindowSurfaceController { try { transaction.hide(mSurfaceControl); if (mAnimator.mIsWallpaper) { + final DisplayContent dc = mAnimator.mWin.getDisplayContent(); EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE, - mAnimator.mWin.getDisplayId(), 0 /* request hidden */); + dc.mDisplayId, 0 /* request hidden */, + String.valueOf(dc.mWallpaperController.getWallpaperTarget())); } } catch (RuntimeException e) { Slog.w(TAG, "Exception hiding surface in " + this); @@ -266,8 +268,10 @@ class WindowSurfaceController { setShown(true); t.show(mSurfaceControl); if (mAnimator.mIsWallpaper) { + final DisplayContent dc = mAnimator.mWin.getDisplayContent(); EventLog.writeEvent(EventLogTags.WM_WALLPAPER_SURFACE, - mAnimator.mWin.getDisplayId(), 1 /* request shown */); + dc.mDisplayId, 1 /* request shown */, + String.valueOf(dc.mWallpaperController.getWallpaperTarget())); } return true; } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 437c9344d793..bbb21f8122c5 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -362,7 +362,7 @@ class WindowToken extends WindowContainer<WindowState> { @Override void assignLayer(SurfaceControl.Transaction t, int layer) { if (mRoundedCornerOverlay) { - super.assignLayer(t, WindowManagerPolicy.SCREEN_DECOR_DISPLAY_OVERLAY_LAYER); + super.assignLayer(t, WindowManagerPolicy.COLOR_FADE_LAYER + 1); } else { super.assignLayer(t, layer); } @@ -372,7 +372,7 @@ class WindowToken extends WindowContainer<WindowState> { SurfaceControl.Builder makeSurface() { final SurfaceControl.Builder builder = super.makeSurface(); if (mRoundedCornerOverlay) { - builder.setParent(getDisplayContent().getOverlayLayer()); + builder.setParent(null); } return builder; } diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java b/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java index dcffc9e73c0e..f041fbd7bf90 100644 --- a/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java +++ b/services/smartspace/java/com/android/server/smartspace/SmartspacePerUserService.java @@ -334,18 +334,7 @@ public class SmartspacePerUserService extends @NonNull private final SmartspaceConfig mSmartspaceConfig; private final RemoteCallbackList<ISmartspaceCallback> mCallbacks = - new RemoteCallbackList<ISmartspaceCallback>() { - @Override - public void onCallbackDied(ISmartspaceCallback callback) { - if (DEBUG) { - Slog.d(TAG, "Binder died for session Id=" + mSessionId - + " and callback=" + callback.asBinder()); - } - if (mCallbacks.getRegisteredCallbackCount() == 0) { - destroy(); - } - } - }; + new RemoteCallbackList<>(); SmartspaceSessionInfo( @NonNull final SmartspaceSessionId id, diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index 494246491e47..67ef7f5bded8 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -722,6 +722,25 @@ public class AlarmManagerServiceTest { } @Test + public void testAlarmBroadcastOption() throws Exception { + final long triggerTime = mNowElapsedTest + 5000; + final PendingIntent alarmPi = getNewMockPendingIntent(); + setTestAlarm(ELAPSED_REALTIME_WAKEUP, triggerTime, alarmPi); + + mNowElapsedTest = mTestTimer.getElapsed(); + mTestTimer.expire(); + + final ArgumentCaptor<PendingIntent.OnFinished> onFinishedCaptor = + ArgumentCaptor.forClass(PendingIntent.OnFinished.class); + final ArgumentCaptor<Bundle> optionsCaptor = ArgumentCaptor.forClass(Bundle.class); + verify(alarmPi).send(eq(mMockContext), eq(0), any(Intent.class), + onFinishedCaptor.capture(), any(Handler.class), isNull(), + optionsCaptor.capture()); + assertTrue(optionsCaptor.getValue() + .getBoolean(BroadcastOptions.KEY_ALARM_BROADCAST, false)); + } + + @Test public void testUpdateConstants() { setDeviceConfigLong(KEY_MIN_FUTURITY, 5); setDeviceConfigLong(KEY_MIN_INTERVAL, 10); diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml index 158bd39a4fd0..7092092fef25 100644 --- a/services/tests/servicestests/AndroidTest.xml +++ b/services/tests/servicestests/AndroidTest.xml @@ -50,5 +50,6 @@ <option name="package" value="com.android.frameworks.servicestests" /> <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> <option name="hidden-api-checks" value="false"/> + <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" /> </test> </configuration> diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java index ece0a627f051..b0738fdb78d0 100644 --- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java +++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java @@ -17,7 +17,11 @@ package com.android.server.display; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.PropertyInvalidatedCache; @@ -45,18 +49,21 @@ public class LogicalDisplayTest { private LogicalDisplay mLogicalDisplay; private DisplayDevice mDisplayDevice; + private final DisplayDeviceInfo mDisplayDeviceInfo = new DisplayDeviceInfo(); @Before public void setUp() { // Share classloader to allow package private access. System.setProperty("dexmaker.share_classloader", "true"); mDisplayDevice = mock(DisplayDevice.class); - DisplayDeviceInfo displayDeviceInfo = new DisplayDeviceInfo(); - displayDeviceInfo.width = DISPLAY_WIDTH; - displayDeviceInfo.height = DISPLAY_HEIGHT; - displayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice); - when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(displayDeviceInfo); + + mDisplayDeviceInfo.copyFrom(new DisplayDeviceInfo()); + mDisplayDeviceInfo.width = DISPLAY_WIDTH; + mDisplayDeviceInfo.height = DISPLAY_HEIGHT; + mDisplayDeviceInfo.flags = DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; + mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL; + when(mDisplayDevice.getDisplayDeviceInfoLocked()).thenReturn(mDisplayDeviceInfo); // Disable binder caches in this process. PropertyInvalidatedCache.disableForTestMode(); @@ -103,4 +110,33 @@ public class LogicalDisplayTest { mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition()); } + + @Test + public void testDisplayInputFlags() { + SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT)); + reset(t); + + mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_NONE; + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + verify(t).setDisplayFlags(any(), eq(0)); + reset(t); + + mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL; + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT)); + reset(t); + + mLogicalDisplay.setPhase(LogicalDisplay.DISPLAY_PHASE_DISABLED); + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + verify(t).setDisplayFlags(any(), eq(0)); + reset(t); + + mLogicalDisplay.setPhase(LogicalDisplay.DISPLAY_PHASE_ENABLED); + mDisplayDeviceInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; + mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false); + verify(t).setDisplayFlags(any(), eq(SurfaceControl.DISPLAY_RECEIVES_INPUT)); + reset(t); + } } diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index c016406fc96a..308a4b67de24 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -928,6 +928,7 @@ public class AppStandbyControllerTests { } @Test + @FlakyTest(bugId = 185169504) public void testNotificationEvent_quotaBump() throws Exception { mInjector.mSettingsBuilder .setBoolean("trigger_quota_bump_on_notification_seen", true); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 22721a1bcc92..b1b323b734bd 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -3380,39 +3380,98 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testSnoozeRunnable_reSnoozeASingleSnoozedNotification() throws Exception { + public void testSnoozeRunnable_tooManySnoozed_singleNotification() { final NotificationRecord notification = generateNotificationRecord( mTestNotificationChannel, 1, null, true); mService.addNotification(notification); - when(mSnoozeHelper.getNotification(any())).thenReturn(notification); + + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); + when(mSnoozeHelper.canSnooze(1)).thenReturn(false); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( - notification.getKey(), 100, null); + notification.getKey(), 100, null); snoozeNotificationRunnable.run(); - NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable2 = + + verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong()); + assertThat(mService.getNotificationRecordCount()).isEqualTo(1); + } + + @Test + public void testSnoozeRunnable_tooManySnoozed_singleGroupChildNotification() { + final NotificationRecord notification = generateNotificationRecord( + mTestNotificationChannel, 1, "group", true); + final NotificationRecord notificationChild = generateNotificationRecord( + mTestNotificationChannel, 1, "group", false); + mService.addNotification(notification); + mService.addNotification(notificationChild); + + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); + when(mSnoozeHelper.canSnooze(2)).thenReturn(false); + + NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = + mService.new SnoozeNotificationRunnable( + notificationChild.getKey(), 100, null); + snoozeNotificationRunnable.run(); + + verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong()); + assertThat(mService.getNotificationRecordCount()).isEqualTo(2); + } + + @Test + public void testSnoozeRunnable_tooManySnoozed_summaryNotification() { + final NotificationRecord notification = generateNotificationRecord( + mTestNotificationChannel, 1, "group", true); + final NotificationRecord notificationChild = generateNotificationRecord( + mTestNotificationChannel, 12, "group", false); + final NotificationRecord notificationChild2 = generateNotificationRecord( + mTestNotificationChannel, 13, "group", false); + mService.addNotification(notification); + mService.addNotification(notificationChild); + mService.addNotification(notificationChild2); + + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); + when(mSnoozeHelper.canSnooze(3)).thenReturn(false); + + NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = + mService.new SnoozeNotificationRunnable( + notification.getKey(), 100, null); + snoozeNotificationRunnable.run(); + + verify(mSnoozeHelper, never()).snooze(any(NotificationRecord.class), anyLong()); + assertThat(mService.getNotificationRecordCount()).isEqualTo(3); + } + + @Test + public void testSnoozeRunnable_reSnoozeASingleSnoozedNotification() { + final NotificationRecord notification = generateNotificationRecord( + mTestNotificationChannel, 1, null, true); + mService.addNotification(notification); + when(mSnoozeHelper.getNotification(any())).thenReturn(notification); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); + + NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( notification.getKey(), 100, null); snoozeNotificationRunnable.run(); + snoozeNotificationRunnable.run(); // snooze twice verify(mSnoozeHelper, times(2)).snooze(any(NotificationRecord.class), anyLong()); } @Test - public void testSnoozeRunnable_reSnoozeASnoozedNotificationWithGroupKey() throws Exception { + public void testSnoozeRunnable_reSnoozeASnoozedNotificationWithGroupKey() { final NotificationRecord notification = generateNotificationRecord( mTestNotificationChannel, 1, "group", true); mService.addNotification(notification); when(mSnoozeHelper.getNotification(any())).thenReturn(notification); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( notification.getKey(), 100, null); snoozeNotificationRunnable.run(); - NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable2 = - mService.new SnoozeNotificationRunnable( - notification.getKey(), 100, null); snoozeNotificationRunnable.run(); // snooze twice @@ -3430,6 +3489,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mSnoozeHelper.getNotification(any())).thenReturn(notification); when(mSnoozeHelper.getNotifications( anyString(), anyString(), anyInt())).thenReturn(new ArrayList<>()); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -3439,8 +3499,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { .thenReturn(new ArrayList<>(Arrays.asList(notification, notification2))); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable2 = mService.new SnoozeNotificationRunnable( - notification.getKey(), 100, null); - snoozeNotificationRunnable.run(); + notification2.getKey(), 100, null); + snoozeNotificationRunnable2.run(); // snooze twice verify(mSnoozeHelper, times(4)).snooze(any(NotificationRecord.class), anyLong()); @@ -3454,6 +3514,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel, 2, "group", false); mService.addNotification(grouped); mService.addNotification(nonGrouped); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -3483,6 +3544,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(parent); mService.addNotification(child); mService.addNotification(child2); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -3504,6 +3566,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(parent); mService.addNotification(child); mService.addNotification(child2); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -3529,6 +3592,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mTestNotificationChannel, 2, "group", false); mService.addNotification(parent); mService.addNotification(child); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( @@ -3556,6 +3620,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { final NotificationRecord child = generateNotificationRecord( mTestNotificationChannel, 2, "group", false); mService.addNotification(child); + when(mSnoozeHelper.canSnooze(anyInt())).thenReturn(true); NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable = mService.new SnoozeNotificationRunnable( diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java index 4c7e8433b15b..f2b1dc9132d5 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java @@ -18,6 +18,7 @@ package com.android.server.notification; import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; +import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED; import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET; import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.PERMISSION_DENIED; @@ -74,6 +75,8 @@ public class PermissionHelperTest extends UiServiceTestCase { private PermissionHelper mPermissionHelper; + private static final int USER_FLAG_MASK = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED; + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); @@ -183,7 +186,8 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).grantRuntimePermission( "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10); + USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, + FLAG_PERMISSION_USER_SET, true, 10); } @Test @@ -201,7 +205,8 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).grantRuntimePermission( "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10); + USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, + FLAG_PERMISSION_USER_SET, true, 10); } @Test @@ -214,7 +219,8 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).revokeRuntimePermission( eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString()); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - FLAG_PERMISSION_USER_SET, FLAG_PERMISSION_USER_SET, true, 10); + USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, + FLAG_PERMISSION_USER_SET, true, 10); } @Test @@ -227,7 +233,7 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).grantRuntimePermission( "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - 0, FLAG_PERMISSION_USER_SET, true, 10); + USER_FLAG_MASK, 0, true, 10); } @Test @@ -240,7 +246,8 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mPermManager).revokeRuntimePermission( eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString()); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - 0, FLAG_PERMISSION_USER_SET, true, 10); + USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0, + true, 10); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java index 2ae2ef7162a5..7817e8176e76 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java @@ -15,10 +15,12 @@ */ package com.android.server.notification; +import static com.android.server.notification.SnoozeHelper.CONCURRENT_SNOOZE_LIMIT; import static com.android.server.notification.SnoozeHelper.EXTRA_KEY; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; @@ -38,7 +40,6 @@ import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; -import android.os.SystemClock; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; @@ -49,7 +50,6 @@ import android.util.Xml; import androidx.test.runner.AndroidJUnit4; -import com.android.internal.util.FastXmlSerializer; import com.android.server.UiServiceTestCase; import com.android.server.pm.PackageManagerService; @@ -59,9 +59,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlSerializer; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; @@ -197,18 +195,6 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test - public void testCleanupContextShouldRemovePersistedRecord() { - NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); - mSnoozeHelper.snooze(r, "context"); - mSnoozeHelper.cleanupPersistedContext(r.getSbn().getKey()); - assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification( - r.getUser().getIdentifier(), - r.getSbn().getPackageName(), - r.getSbn().getKey() - )); - } - - @Test public void testReadNoneSnoozedNotification() throws XmlPullParserException, IOException, InterruptedException { NotificationRecord r = getNotificationRecord( @@ -218,8 +204,9 @@ public class SnoozeHelperTest extends UiServiceTestCase { assertEquals("should see a zero value for unsnoozed notification", 0L, mSnoozeHelper.getSnoozeTimeForUnpostedNotification( - UserHandle.SYSTEM.getIdentifier(), - "not_my_package", r.getKey()).longValue()); + UserHandle.SYSTEM.getIdentifier(), "not_my_package", + getNotificationRecord("not_my_package", 1, "one", + UserHandle.SYSTEM).getKey()).longValue()); } @Test @@ -281,6 +268,22 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test + public void testSnoozeLimit() { + for (int i = 0; i < CONCURRENT_SNOOZE_LIMIT; i++ ) { + NotificationRecord r = getNotificationRecord("pkg", i, i+"", UserHandle.SYSTEM); + + assertTrue("cannot snooze record " + i, mSnoozeHelper.canSnooze(1)); + + if (i % 2 == 0) { + mSnoozeHelper.snooze(r, null); + } else { + mSnoozeHelper.snooze(r, 9000); + } + } + assertFalse(mSnoozeHelper.canSnooze(1)); + } + + @Test public void testCancelByApp() throws Exception { NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); @@ -575,7 +578,7 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test - public void testClearData() { + public void testClearData_userPackage() { // snooze 2 from same package NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM); @@ -599,17 +602,72 @@ public class SnoozeHelperTest extends UiServiceTestCase { } @Test + public void testClearData_user() { + // snooze 2 from same package + NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); + NotificationRecord r2 = getNotificationRecord("pkg2", 2, "two", UserHandle.SYSTEM); + NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM); + NotificationRecord r4 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL); + mSnoozeHelper.snooze(r, 1000); + mSnoozeHelper.snooze(r2, 1000); + mSnoozeHelper.snooze(r3, "until"); + mSnoozeHelper.snooze(r4, "until"); + + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey())); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey())); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_ALL, r4.getSbn().getPackageName(), r4.getKey())); + + // clear data + mSnoozeHelper.clearData(UserHandle.USER_SYSTEM); + + // nothing in USER_SYSTEM snoozed; alarms canceled + assertFalse(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); + assertFalse(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r2.getSbn().getPackageName(), r2.getKey())); + assertFalse(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey())); + assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, r4.getSbn().getPackageName(), r4.getKey())); + + assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification( + r3.getUser().getIdentifier(), r3.getSbn().getPackageName(), + r3.getSbn().getKey())); + assertNotNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification( + r4.getUser().getIdentifier(), r4.getSbn().getPackageName(), + r4.getSbn().getKey())); + assertEquals(0L, mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r.getUser().getIdentifier(), r.getSbn().getPackageName(), + r.getSbn().getKey()).longValue()); + assertEquals(0L, mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r2.getUser().getIdentifier(), r2.getSbn().getPackageName(), + r2.getSbn().getKey()).longValue()); + + // 2 for initial timed-snoozes, once each for canceling the USER_SYSTEM snoozes + verify(mAm, times(5)).cancel(any(PendingIntent.class)); + } + + @Test public void testClearData_otherRecordsUntouched() { // 2 packages, 2 users NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM); + NotificationRecord rb = getNotificationRecord("pkg", 1, "oneb", UserHandle.SYSTEM); NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.ALL); NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM); mSnoozeHelper.snooze(r, 1000); + mSnoozeHelper.snooze(rb, "until"); mSnoozeHelper.snooze(r2, 1000); mSnoozeHelper.snooze(r3, 1000); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); assertTrue(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, rb.getSbn().getPackageName(), rb.getKey())); + assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_ALL, r2.getSbn().getPackageName(), r2.getKey())); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey())); @@ -619,12 +677,22 @@ public class SnoozeHelperTest extends UiServiceTestCase { assertFalse(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r.getSbn().getPackageName(), r.getKey())); + assertFalse(mSnoozeHelper.isSnoozed( + UserHandle.USER_SYSTEM, rb.getSbn().getPackageName(), rb.getKey())); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_ALL, r2.getSbn().getPackageName(), r2.getKey())); assertTrue(mSnoozeHelper.isSnoozed( UserHandle.USER_SYSTEM, r3.getSbn().getPackageName(), r3.getKey())); + + assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification( + rb.getUser().getIdentifier(), rb.getSbn().getPackageName(), + rb.getSbn().getKey())); + assertEquals(0L, mSnoozeHelper.getSnoozeTimeForUnpostedNotification( + r.getUser().getIdentifier(), r.getSbn().getPackageName(), + r.getSbn().getKey()).longValue()); + // once for each initial snooze, once for canceling one snooze - verify(mAm, times(4)).cancel(any(PendingIntent.class)); + verify(mAm, times(5)).cancel(any(PendingIntent.class)); } private NotificationRecord getNotificationRecord(String pkg, int id, String tag, 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 0c3b270518cf..3f3d01a14f80 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -49,7 +49,6 @@ import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.Process.NOBODY_UID; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.InsetsState.ITYPE_IME; -import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; @@ -2539,21 +2538,6 @@ public class ActivityRecordTests extends WindowTestsBase { } @Test - public void testStuckExitingWindow() { - final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, - "closingWindow"); - closingWindow.mAnimatingExit = true; - closingWindow.mRemoveOnExit = true; - closingWindow.mActivityRecord.commitVisibility( - false /* visible */, true /* performLayout */); - - // We pretended that we were running an exit animation, but that should have been cleared up - // by changing visibility of ActivityRecord - closingWindow.removeIfPossible(); - assertTrue(closingWindow.mRemoved); - } - - @Test public void testSetOrientation() { final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build(); activity.setVisible(true); @@ -3149,6 +3133,7 @@ public class ActivityRecordTests extends WindowTestsBase { mDisplayContent.mOpeningApps.clear(); app.mActivityRecord.commitVisibility(false, false); app.mActivityRecord.onWindowsGone(); + mDisplayContent.computeImeTargetIfNeeded(app.mActivityRecord); assertTrue(app.mActivityRecord.mLastImeShown); assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); 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 f2d6273f2b26..513791d2b8a5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -47,6 +47,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.clearInvocations; @@ -89,6 +90,7 @@ public class AppTransitionControllerTest extends WindowTestsBase { @Before public void setUp() throws Exception { + assumeFalse(WindowManagerService.sEnableShellTransitions); mAppTransitionController = new AppTransitionController(mWm, mDisplayContent); } 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 40e266c71328..11a7c7ddf778 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -2118,7 +2118,6 @@ public class DisplayContentTests extends WindowTestsBase { final WindowState appWin2 = createWindow(null, TYPE_BASE_APPLICATION, act2, "appWin2"); appWin2.setHasSurface(true); assertTrue(appWin2.canBeImeTarget()); - doReturn(true).when(appWin1).isClosing(); doReturn(true).when(appWin1).inTransitionSelfOrParent(); // Test step 3: Verify appWin2 will be the next IME target and the IME snapshot surface will diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index ffa21fadff6b..6c161cf088f7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -27,6 +27,7 @@ import static android.view.InsetsState.ITYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; @@ -44,12 +45,15 @@ import static org.mockito.Mockito.verify; import android.graphics.Rect; import android.platform.test.annotations.Presubmit; +import android.util.SparseArray; import android.view.InsetsSourceControl; import android.view.InsetsState; import android.view.InsetsVisibilities; import androidx.test.filters.SmallTest; +import com.android.internal.util.function.TriConsumer; + import org.junit.Test; import org.junit.runner.RunWith; @@ -269,15 +273,18 @@ public class InsetsStateControllerTest extends WindowTestsBase { @Test public void testImeForDispatch() { final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar"); - final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime"); + final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, "ime"); // IME cannot be the IME target. ime.mAttrs.flags |= FLAG_NOT_FOCUSABLE; WindowContainerInsetsSourceProvider statusBarProvider = getController().getSourceProvider(ITYPE_STATUS_BAR); - statusBarProvider.setWindowContainer(statusBar, null, ((displayFrames, windowState, rect) -> + final SparseArray<TriConsumer<DisplayFrames, WindowContainer, Rect>> imeOverrideProviders = + new SparseArray<>(); + imeOverrideProviders.put(TYPE_INPUT_METHOD, ((displayFrames, windowState, rect) -> rect.set(0, 1, 2, 3))); + statusBarProvider.setWindowContainer(statusBar, null, imeOverrideProviders); getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null); statusBar.setControllableInsetProvider(statusBarProvider); statusBar.updateSourceFrame(statusBar.getFrame()); 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 324e244c46f5..f2640d2dc404 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -1496,6 +1496,79 @@ public class SizeCompatTests extends WindowTestsBase { } @Test + public void testComputeConfigResourceOverrides_unresizableApp() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT); + + final Rect activityBounds = new Rect(mActivity.getBounds()); + + int originalScreenWidthDp = mActivity.getConfiguration().screenWidthDp; + int originalScreenHeighthDp = mActivity.getConfiguration().screenHeightDp; + + // App should launch in fixed orientation letterbox. + // Activity bounds should be 700x1400 with the ratio as the display. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); + assertFitted(); + assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp); + assertTrue(originalScreenWidthDp < originalScreenHeighthDp); + + // Rotate display to portrait. + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + + // After we rotate, the activity should go in the size-compat mode and report the same + // configuration values. + assertScaled(); + assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp); + assertEquals(originalScreenWidthDp, mActivity.getConfiguration().screenWidthDp); + assertEquals(originalScreenHeighthDp, mActivity.getConfiguration().screenHeightDp); + + // Restart activity + mActivity.restartProcessIfVisible(); + + // Now configuration should be updated + assertFitted(); + assertNotEquals(originalScreenWidthDp, mActivity.getConfiguration().screenWidthDp); + assertNotEquals(originalScreenHeighthDp, mActivity.getConfiguration().screenHeightDp); + assertEquals(mActivity.getConfiguration().screenWidthDp, + mActivity.getConfiguration().smallestScreenWidthDp); + } + + @Test + public void testComputeConfigResourceOverrides_resizableFixedOrientationActivity() { + // Set up a display in landscape and ignoring orientation request. + setUpDisplaySizeWithApp(2800, 1400); + mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + // Portrait fixed app without max aspect. + prepareLimitedBounds(mActivity, SCREEN_ORIENTATION_PORTRAIT, false /* isUnresizable */); + + final Rect activityBounds = new Rect(mActivity.getBounds()); + + int originalScreenWidthDp = mActivity.getConfiguration().screenWidthDp; + int originalScreenHeighthDp = mActivity.getConfiguration().screenHeightDp; + + // App should launch in fixed orientation letterbox. + // Activity bounds should be 700x1400 with the ratio as the display. + assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio()); + assertFitted(); + assertEquals(originalScreenWidthDp, mActivity.getConfiguration().smallestScreenWidthDp); + assertTrue(originalScreenWidthDp < originalScreenHeighthDp); + + // Rotate display to portrait. + rotateDisplay(mActivity.mDisplayContent, ROTATION_90); + + // Now configuration should be updated + assertFitted(); + assertNotEquals(originalScreenWidthDp, mActivity.getConfiguration().screenWidthDp); + assertNotEquals(originalScreenHeighthDp, mActivity.getConfiguration().screenHeightDp); + assertEquals(mActivity.getConfiguration().screenWidthDp, + mActivity.getConfiguration().smallestScreenWidthDp); + } + + @Test public void testSplitAspectRatioForUnresizablePortraitApps() { // Set up a display in landscape and ignoring orientation request. int screenWidth = 1600; 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 46e21f1ffdbc..1f03039de72b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -268,6 +268,20 @@ public class TaskTests extends WindowTestsBase { assertFalse(task.hasChild()); // In real case, the task should be preserved for adding new activity. assertTrue(task.isAttached()); + + final ActivityRecord activityA = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord activityB = new ActivityBuilder(mAtm).setTask(task).build(); + final ActivityRecord activityC = new ActivityBuilder(mAtm).setTask(task).build(); + activityA.setState(ActivityRecord.State.STOPPED, "test"); + activityB.setState(ActivityRecord.State.PAUSED, "test"); + activityC.setState(ActivityRecord.State.RESUMED, "test"); + doReturn(true).when(activityB).shouldBeVisibleUnchecked(); + doReturn(true).when(activityC).shouldBeVisibleUnchecked(); + activityA.getConfiguration().densityDpi += 100; + assertTrue(task.performClearTop(activityA, 0 /* launchFlags */).finishing); + // The bottom activity should destroy directly without relaunch for config change. + assertEquals(ActivityRecord.State.DESTROYING, activityA.getState()); + verify(activityA, never()).startRelaunching(); } @Test @@ -691,7 +705,8 @@ public class TaskTests extends WindowTestsBase { final ActivityRecord.CompatDisplayInsets compatInsets = new ActivityRecord.CompatDisplayInsets( display, activity, /* fixedOrientationBounds= */ null); - task.computeConfigResourceOverrides(inOutConfig, parentConfig, compatInsets); + task.computeConfigResourceOverrides( + inOutConfig, parentConfig, compatInsets, /* areBoundsLetterboxed */ true); assertEquals(largerLandscapeBounds, inOutConfig.windowConfiguration.getAppBounds()); final float density = parentConfig.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; 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 e1fbf96b2e71..c323e02ad149 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java @@ -30,6 +30,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 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.window.TransitionInfo.FLAG_IS_EMBEDDED; import static android.window.TransitionInfo.FLAG_IS_WALLPAPER; import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; @@ -61,8 +62,10 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.view.SurfaceControl; import android.window.IDisplayAreaOrganizer; +import android.window.ITaskFragmentOrganizer; import android.window.ITaskOrganizer; import android.window.ITransitionPlayer; +import android.window.TaskFragmentOrganizer; import android.window.TransitionInfo; import androidx.test.filters.SmallTest; @@ -1005,6 +1008,43 @@ public class TransitionTests extends WindowTestsBase { assertTrue(openTransition.allReady()); } + @Test + public void testIsEmbeddedChange() { + final Transition transition = createTestTransition(TRANSIT_OPEN); + final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges; + final ArraySet<WindowContainer> participants = transition.mParticipants; + + final Task task = createTask(mDisplayContent); + final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run); + mAtm.mTaskFragmentOrganizerController.registerOrganizer( + ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder())); + final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .createActivityCount(2) + .setOrganizer(organizer) + .build(); + final ActivityRecord closingActivity = embeddedTf.getBottomMostActivity(); + final ActivityRecord openingActivity = embeddedTf.getTopMostActivity(); + // Start states. + changes.put(embeddedTf, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); + changes.put(closingActivity, new Transition.ChangeInfo(true /* vis */, false /* exChg */)); + changes.put(openingActivity, new Transition.ChangeInfo(false /* vis */, true /* exChg */)); + // End states. + closingActivity.mVisibleRequested = false; + openingActivity.mVisibleRequested = true; + + participants.add(closingActivity); + participants.add(openingActivity); + final ArrayList<WindowContainer> targets = Transition.calculateTargets( + participants, changes); + final TransitionInfo info = Transition.calculateTransitionInfo( + transition.mType, 0 /* flags */, targets, changes, mMockT); + + assertEquals(2, info.getChanges().size()); + assertTrue((info.getChanges().get(0).getFlags() & FLAG_IS_EMBEDDED) != 0); + assertTrue((info.getChanges().get(1).getFlags() & FLAG_IS_EMBEDDED) != 0); + } + 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/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 9c2aac066084..540741217ae8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -459,6 +459,23 @@ public class WindowOrganizerTests extends WindowTestsBase { } @Test + public void testRegisterTaskOrganizerWithExistingTasks_noSurfaceControl() + throws RemoteException { + final Task rootTask = createRootTask(); + final Task task = createTask(rootTask); + final Task rootTask2 = createRootTask(); + final Task task2 = createTask(rootTask2); + rootTask2.setSurfaceControl(null); + ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); + final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); + assertContainsTasks(existingTasks, rootTask); + + // Verify we don't get onTaskAppeared if we are returned the tasks + verify(organizer, never()) + .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); + } + + @Test public void testTaskTransaction() { removeGlobalMinSizeRestriction(); final Task rootTask = new TaskBuilder(mSupervisor) 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 fb7400bc83ef..446ec8b2680b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -720,6 +720,17 @@ public class WindowStateTests extends WindowTestsBase { outWaitingForDrawn.clear(); invisibleApp.requestDrawIfNeeded(outWaitingForDrawn); assertTrue(outWaitingForDrawn.isEmpty()); + + // Drawn state should not be changed for insets change when screen is off. + spyOn(mWm.mPolicy); + doReturn(false).when(mWm.mPolicy).isScreenOn(); + makeWindowVisibleAndDrawn(startingApp); + startingApp.getConfiguration().orientation = 0; // Reset to be the same as last reported. + startingApp.getWindowFrames().setInsetsChanged(true); + startingApp.updateResizingWindowIfNeeded(); + assertTrue(mWm.mResizingWindows.contains(startingApp)); + assertTrue(startingApp.isDrawn()); + assertFalse(startingApp.getOrientationChanging()); } @UseTestDisplay(addWindows = W_ABOVE_ACTIVITY) @@ -992,6 +1003,7 @@ public class WindowStateTests extends WindowTestsBase { assertTrue(app.mActivityRecord.mImeInsetsFrozenUntilStartInput); // Verify the IME insets is visible on app, but not for app2 during app task switching. + mDisplayContent.computeImeTargetIfNeeded(app.mActivityRecord); assertTrue(app.getInsetsState().getSource(ITYPE_IME).isVisible()); assertFalse(app2.getInsetsState().getSource(ITYPE_IME).isVisible()); } 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 8a539cd3ddff..0cbf1b2c7cc8 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -811,7 +811,7 @@ class WindowTestsBase extends SystemServiceTestsBase { } @Override - public void topFocusedWindowChanged(String packageName, + public void topFocusedWindowChanged(ComponentName component, InsetsVisibilities requestedVisibilities) { } }; diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java index 4924a82c385f..423022599de6 100644 --- a/telephony/common/com/android/internal/telephony/SmsApplication.java +++ b/telephony/common/com/android/internal/telephony/SmsApplication.java @@ -1146,4 +1146,35 @@ public final class SmsApplication { } return null; } + + /** + * Check if a package is default mms app (or equivalent, like bluetooth) + * + * @param context context from the calling app + * @param packageName the name of the package to be checked + * @return true if the package is default mms app or bluetooth + */ + @UnsupportedAppUsage + public static boolean isDefaultMmsApplication(Context context, String packageName) { + if (packageName == null) { + return false; + } + String defaultMmsPackage = getDefaultMmsApplicationPackageName(context); + String bluetoothPackageName = context.getResources() + .getString(com.android.internal.R.string.config_systemBluetoothStack); + + if ((defaultMmsPackage != null && defaultMmsPackage.equals(packageName)) + || bluetoothPackageName.equals(packageName)) { + return true; + } + return false; + } + + private static String getDefaultMmsApplicationPackageName(Context context) { + ComponentName component = getDefaultMmsApplication(context, false); + if (component != null) { + return component.getPackageName(); + } + return null; + } } diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java index 38becc6af0dc..297940e9c8c6 100644 --- a/telephony/java/android/telephony/CellSignalStrengthNr.java +++ b/telephony/java/android/telephony/CellSignalStrengthNr.java @@ -49,7 +49,7 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa private static final String TAG = "CellSignalStrengthNr"; // Lifted from Default carrier configs and max range of SSRSRP - // Boundaries: [-140 dB, -44 dB] + // Boundaries: [-156 dB, -31 dB] private int[] mSsRsrpThresholds = new int[] { -110, /* SIGNAL_STRENGTH_POOR */ -90, /* SIGNAL_STRENGTH_MODERATE */ @@ -173,14 +173,14 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa */ public CellSignalStrengthNr(int csiRsrp, int csiRsrq, int csiSinr, int csiCqiTableIndex, List<Byte> csiCqiReport, int ssRsrp, int ssRsrq, int ssSinr) { - mCsiRsrp = inRangeOrUnavailable(csiRsrp, -140, -44); + mCsiRsrp = inRangeOrUnavailable(csiRsrp, -156, -31); mCsiRsrq = inRangeOrUnavailable(csiRsrq, -20, -3); mCsiSinr = inRangeOrUnavailable(csiSinr, -23, 23); mCsiCqiTableIndex = inRangeOrUnavailable(csiCqiTableIndex, 1, 3); mCsiCqiReport = csiCqiReport.stream() - .map(cqi -> new Integer(inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 0, 15))) + .map(cqi -> inRangeOrUnavailable(Byte.toUnsignedInt(cqi), 0, 15)) .collect(Collectors.toList()); - mSsRsrp = inRangeOrUnavailable(ssRsrp, -140, -44); + mSsRsrp = inRangeOrUnavailable(ssRsrp, -156, -31); mSsRsrq = inRangeOrUnavailable(ssRsrq, -43, 20); mSsSinr = inRangeOrUnavailable(ssSinr, -23, 40); updateLevel(null, null); @@ -212,8 +212,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** - * Reference: 3GPP TS 38.215. - * Range: -140 dBm to -44 dBm. + * Reference: 3GPP TS 38.133 10.1.6.1. + * Range: -156 dBm to -31 dBm. * @return SS reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported * value. */ @@ -242,8 +242,8 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa } /** - * Reference: 3GPP TS 38.215. - * Range: -140 dBm to -44 dBm. + * Reference: 3GPP TS 38.133 10.1.6.1. + * Range: -156 dBm to -31 dBm. * @return CSI reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported * value. */ diff --git a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java index 24dfbd028d03..a004cc3a1642 100644 --- a/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java +++ b/telephony/java/android/telephony/PhoneNumberFormattingTextWatcher.java @@ -21,6 +21,7 @@ import android.os.Build; import android.text.Editable; import android.text.Selection; import android.text.TextWatcher; +import android.text.style.TtsSpan; import com.android.i18n.phonenumbers.AsYouTypeFormatter; import com.android.i18n.phonenumbers.PhoneNumberUtil; @@ -119,6 +120,13 @@ public class PhoneNumberFormattingTextWatcher implements TextWatcher { } mSelfChange = false; } + + //remove previous TTS spans + TtsSpan[] ttsSpans = s.getSpans(0, s.length(), TtsSpan.class); + for (TtsSpan ttsSpan : ttsSpans) { + s.removeSpan(ttsSpan); + } + PhoneNumberUtils.ttsSpanAsPhoneNumber(s, 0, s.length()); } diff --git a/telephony/java/android/telephony/UiccSlotInfo.java b/telephony/java/android/telephony/UiccSlotInfo.java index 06c5b5cfeda1..5e02532e85a8 100644 --- a/telephony/java/android/telephony/UiccSlotInfo.java +++ b/telephony/java/android/telephony/UiccSlotInfo.java @@ -129,7 +129,7 @@ public class UiccSlotInfo implements Parcelable { this.mLogicalSlotIdx = logicalSlotIdx; this.mIsExtendedApduSupported = isExtendedApduSupported; this.mIsRemovable = false; - this.mPortList = null; + this.mPortList = new ArrayList<UiccPortInfo>(); } /** diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index a673807a3f97..1252dc178cb9 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -769,7 +769,7 @@ public class EuiccManager { public static final int ERROR_INSTALL_PROFILE = 10009; /** - * Failed to load profile onto eUICC due to Profile Poicly Rules. + * Failed to load profile onto eUICC due to Profile Policy Rules. * @see #EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE for details */ public static final int ERROR_DISALLOWED_BY_PPR = 10010; |